
Índice
Introdução
Generics e Tipos Restritos em Delphi:
Os Generics em Delphi proporcionam grande flexibilidade ao código, permitindo criar classes e métodos que podem trabalhar com diferentes tipos de dados sem a necessidade de duplicação de código. No entanto, em algumas situações, é desejável restringir o tipo de dado que um Generic pode aceitar, garantindo que ele funcione apenas com determinados tipos de dados ou interfaces específicas. Neste artigo, vamos explorar como restringir tipos genéricos em Delphi usando a cláusula where
, além de discutir as diferenças entre restrições de interface, classe e tipos primitivos.
O Que São Generics?
Generics permitem que você crie classes, métodos e registros que podem ser utilizados com diferentes tipos de dados, mantendo a segurança de tipos em tempo de compilação. No entanto, há cenários onde você deseja limitar os tipos que podem ser usados com esses Generics. Em Delphi, isso é feito com a cláusula where
, que adiciona restrições ao tipo genérico.
A Cláusula where
para Restringir Tipos
A cláusula where
em Delphi permite especificar requisitos para o tipo genérico, como exigir que o tipo seja uma classe, um tipo numérico ou que implemente uma interface. Isso traz maior controle sobre os tipos de dados que podem ser passados para a classe ou método genérico, garantindo que certas operações só possam ser realizadas com tipos apropriados.
Repare que a cláusula where
não é uma palavra chave que será utilizada no código, mas sim uma palavra que representa a restrição ao criar um tipo genérico.
A sintaxe básica da cláusula where
é a seguinte:
type TClasseGenerica<T: class> = class end;
Aqui, a cláusula T: class
indica que o tipo genérico T
deve ser uma classe. Isso impede que tipos primitivos como Integer
ou Double
sejam usados com essa classe genérica.
Exemplo de Uso da Cláusula where
Vamos criar um exemplo que utiliza a cláusula where
para restringir o tipo genérico a classes que implementam uma determinada interface.
type //Interface IExemplo = interface procedure MostrarMensagem; end; //Classe genérica que implementa a interface IExemplo e obriga que o tipo genérico seja uma classe TClasseGenerica<T: class, IExemplo> = class private FObjeto: T; public constructor Create(AObjeto: T); procedure Executar; end; constructor TClasseGenerica<T>.Create(AObjeto: T); begin FObjeto := AObjeto; end; procedure TClasseGenerica<T>.Executar; begin FObjeto.MostrarMensagem; end;
No exemplo acima, a classe genérica TClasseGenerica
exige que o tipo T
seja uma classe (class
) e que implemente a interface IExemplo
. Isso garante que, ao instanciar a classe genérica, o tipo fornecido deve ser compatível com as operações da interface.
Para utilizar a classe genérica TClasseGenerica
que implementa a interface IExemplo
, vamos primeiro criar uma classe que implemente essa interface. Assim, nossa classe genérica poderá receber apenas objetos que cumpram os requisitos da interface IExemplo
.
Passo 1: Criar uma Classe que Implemente a Interface IExemplo
Primeiro, precisamos implementar a interface IExemplo
em uma classe. A interface exige o método MostrarMensagem
, então vamos criá-lo para exibir uma mensagem no console.
type //Classe que implementa a interface IExemplo que obriga a ter o método MostrarMensagem TExemploConcreto = class(TInterfacedObject, IExemplo) public procedure MostrarMensagem; end; procedure TExemploConcreto.MostrarMensagem; begin ShowMessage('Mensagem exibida da classe TExemploConcreto.'); end;
Nesse código, TExemploConcreto
implementa a interface IExemplo
e, portanto, deve incluir o método MostrarMensagem
. Esse método exibe uma mensagem simples no console, simulando um comportamento comum de implementação de interface.
Passo 2: Usar a Classe Genérica TClasseGenerica
com TExemploConcreto
Agora que temos uma classe concreta (TExemploConcreto
) que implementa a interface IExemplo
, podemos usar essa classe como um parâmetro genérico para TClasseGenerica
. Vamos instanciar TClasseGenerica
passando TExemploConcreto
como tipo.
procedure TestarGenerics; var ObjConcreto: TExemploConcreto; Generica: TClasseGenerica<TExemploConcreto>; begin // Criar uma instância de TExemploConcreto ObjConcreto := TExemploConcreto.Create; // Instanciar TClasseGenerica com o tipo TExemploConcreto Generica := TClasseGenerica<TExemploConcreto>.Create(ObjConcreto); // Chamar o método Executar para exibir a mensagem Generica.Executar; // Liberar memória Generica.Free; end;
Explicação do Código
TExemploConcreto = class(TInterfacedObject, IExemplo)
: Essa linha declara a classeTExemploConcreto
, que implementa a interfaceIExemplo
. Através deTInterfacedObject
, ela fornece suporte de referência de contagem automática (ARC) e implementação de interface.procedure MostrarMensagem;
: Este é o método da interfaceIExemplo
, exigido para implementar a interface. Ele imprime uma mensagem para o console.Generica := TClasseGenerica<TExemploConcreto>.Create(ObjConcreto);
: Aqui, criamos uma instância deTClasseGenerica
passandoTExemploConcreto
como tipo. Esse exemplo permite que o código genérico seja executado de maneira flexível e independente do tipo específico de objeto, mas garantindo que ele implementeIExemplo
.Generica.Executar;
: Esse método chamaExecutar
da classe genéricaTClasseGenerica
, que, por sua vez, chamaMostrarMensagem
deTExemploConcreto
.
Resultado Esperado
Quando você executa TestarGenerics
, o console exibe a seguinte mensagem:
Mensagem exibida da classe TExemploConcreto.
Esse exemplo demonstra o uso prático dos Generics com restrições de interface. Aqui, o código genérico pode trabalhar com diferentes tipos de classes, desde que implementem IExemplo
. Essa abordagem traz flexibilidade e segurança de tipos ao seu código, permitindo abstrações poderosas em Delphi.
Veja abaixo a ilustração do projeto:

Código fonte do exemplo
Você pode fazer o download do exemplo do projeto através do repositório do github:
https://github.com/Gisele-de-Melo/GenericsTiposRestritos
Diferenças Entre Restrições de Interface, Classe e Tipos Primitivos
Restrições de Interface
Quando você usa a cláusula where
com interfaces, como mostrado no exemplo acima, você garante que o tipo genérico implementa um conjunto específico de métodos. Isso é útil quando você quer trabalhar com tipos diferentes, mas que compartilham um comportamento comum definido pela interface.
Exemplo:
type TGenericaInterface<T: IInterfaceExemplo> = class end;
Restrições de Classe
A restrição de classe (T: class
) permite que o tipo genérico seja apenas classes, excluindo tipos primitivos como inteiros ou strings. Isso é útil em cenários onde você deseja manipular referências de objetos ou garantir que o tipo tenha um comportamento específico de classes, como herança ou polimorfismo.
Exemplo:
type TGenericaClasse<T: class> = class end;
Restrições de Tipos Primitivos
Embora o Delphi não permita diretamente restringir Generics a tipos primitivos (como Integer
ou Double
), você pode criar sobrecargas ou utilizar outra lógica para aplicar restrições, como utilizar RTTI (informações em tempo de execução) para verificar o tipo em tempo de execução.
Vantagens de Usar Restrições de Tipo
- Segurança em Tempo de Compilação: As restrições de tipo garantem que o código seja mais seguro, já que erros de tipo serão capturados durante a compilação, e não em tempo de execução.
- Manutenção do Código: Com as restrições de tipo, o código se torna mais fácil de manter, pois você pode garantir que certas operações só sejam executadas em tipos específicos.
- Reutilização de Código: Ao usar Generics com restrições, você ainda mantém a flexibilidade dos Generics, mas agora com uma camada adicional de controle, tornando o código mais reutilizável em diferentes cenários.
Limitações e Desvantagens
Embora Generics com restrições tragam maior segurança e flexibilidade, há algumas limitações a serem consideradas:
- Complexidade: O uso excessivo de restrições pode aumentar a complexidade do código, tornando-o mais difícil de ler e entender.
- Limitação a Tipos Específicos: Em alguns casos, pode ser necessário usar tipos primitivos, mas as restrições podem limitar essa flexibilidade. Para contornar isso, pode ser necessário adotar padrões diferentes de implementação.
Conclusão
Generics em Delphi são uma ferramenta poderosa para criar código reutilizável e flexível, mas a adição de restrições com a cláusula where
oferece ainda mais controle sobre os tipos permitidos. Ao aplicar essas restrições, como exigir que um tipo implemente uma interface ou seja uma classe, você pode garantir que o código funcione corretamente com os tipos apropriados, promovendo segurança e clareza.
Ao usar Generics com restrições, seu código se torna mais robusto, seguro e fácil de manter. Porém, é importante balancear o uso de restrições para evitar adicionar complexidade desnecessária. Em situações onde você precisa de flexibilidade, mas também quer garantir que apenas certos tipos possam ser usados, Generics com restrições são a solução ideal.
Posts Relacionados
Delphi para Android e iOS
Delphi Start
Programando em Delphi XE
Aprenda programar Delphi
Banco de Dados RAD Delphi
Delphi Programming Projects