: 0cm; TEXT-ALIGN: left" align=left>Tal comportamento pode ser modificado para métodos virtuais e dinâmicos, através do uso das diretivas virtual e dynamic, em conjunto com a diretiva override. Assim, a chamada dos métodos passa a ativar a implementação do tipo em tempo de execução, ou seja, de acordo com o tipo da instância e não o tipo definido para a variável.
O uso de override, garante que apenas uma implementação do método exista em tempo de execução nas classes descendentes, ocultando as implementações dos ancestrais. Porém, esses métodos devem ter exatamente a mesma assinatura, ou seja, mesmo nome do método e mesmo número, ordem e tipos dos parâmetros.
As diretivas virtual e dynamic são equivalentes, sendo diferentes no sentido de que a virtual otimiza a velocidade de acesso e dynamic otimiza o tamanho do código, sendo a virtual a forma mais comum e eficiente de implementar o polimorfismo. Na Listagem 1 demonstra-se o efeito do uso dessas diretivas, através de algumas modificações na hierarquia de Funcionario.
Listagem 1. Definição da hierarquia de Funcionario com método virtual
type
Funcionario = class
function CalcularSalario: real; virtual;
end;
FuncionarioMensalista = class (Funcionario)
function CalcularSalario: real; override;
end;
FuncionarioDiarista = class (Funcionario)
function CalcularSalario: real; override;
end;
Na Listagem 1, utiliza-se na assinatura do método CalcularSalario na classe Funcionario, a diretiva virtual, enquanto que nas classes descendentes utiliza-se a diretiva override. As implementações dos métodos continuam as mesmas.
A Listagem 2 apresenta a utilização dos métodos com a diretiva override. Pressupõe-se que os valores dos campos dos objetos tenham sido modificados entre a criação dos objetos e a execução dos serviços CalcularSalario. A Listagem 2 deve ser implementada em outra unit que não a untClasses, devendo referenciá-la.
Listagem 2. Chamada de métodos virtuais com override
01: var
02: Func1: Funcionario;
03: Func2: FuncionarioDiarista;
04: begin
05: {Funcionario 1}
06: Func1 := FuncionarioMensalista.Create;
07: Func1.CalcularSalario;
08: {Funcionario 2}
09: Func2 := FuncionarioDiarista.Create;
10: Func2.CalcularSalario;
11: end;
Durante a execução do trecho de código apresentado na Listagem 2, as chamadas de métodos nas linhas 07 e 10 ativam a implementação correspondente das classes FuncionarioMensalista e FuncionarioDiarista respectivamente. Embora o tipo de dados da variável Func1 seja Funcionario (linha 02), o objeto armazenado nela é do tipo FuncionarioMensalista (linha 06). Sendo assim, em função da diretiva override, são executadas as implementações relativas às instâncias, e não relativas ao tipo da variável, como no caso dos métodos estáticos.
Vale a pena ressaltar que, para uso da diretiva override, a assinatura dos métodos deve ser exatamente igual, pois de outra forma é apresentado um erro durante a compilação. Além disso, override somente pode ser utilizado em conjunto com virtual ou dynamic, não podendo ser utilizado em métodos estáticos.
Para casos de polimorfismo onde a assinatura do método é diferente nos seus descendentes, podem ser utilizadas duas outras diretivas: overload e reintroduce.
A diretiva overload pode ser utilizada para qualquer tipo de método, seja estático, virtual ou dinâmico. Para métodos virtuais pode-se utilizar a diretiva reintroduce em conjunto para os descendentes. Além de permitir assinaturas diferentes de um mesmo método, a diretiva overload não oculta as implementações do método nos ancestrais, estando assim disponíveis mais de uma implementação simultaneamente.
Para ativar a implementação correta, são analisados os parâmetros utilizados na chamada do método, executando assim a implementação que corresponder a esses parâmetros. Porém, para os métodos virtuais, caso seja necessário, os métodos dos ancestrais podem ser ocultados, utilizando a diretiva reintroduce.
A Listagem 3 apresenta um exemplo do uso das diretivas overload e reintroduce.
Listagem 3. Hierarquia com uso de overload e reintroduce
01: type
02: Funcionario = class
03: {Definições existentes}
04: public
05: function CalcularSalario: real;
06: function CalcularPremio: real; virtual;
07: end;
08: FuncionarioMensalista = class (Funcionario)
09: {Definições existentes}
10: public
11: function CalcularSalario(
pHoraExtra: real): real; overload;
12: function CalcularPremio(
pTaxa: real): real; reintroduce;
13: end;
14: implementation
15: {Implementações existentes}
16: function FuncionarioMensalista.
CalcularSalario(pHoraExtra: real): real;
17: begin
18: result := fValorMes + pHoraExtra;
19: end;
20: function FuncionarioMensalista.
CalcularPremio(pTaxa: real): real;
21: begin
22: result := fValorMes * pTaxa;
23: end;
Nota-se a presença de uma hierarquia de classes, onde a classe Funcionario é ancestral direta de FuncionarioMensalista (linha 08). Na subclasse são alterados os métodos CalcularSalario e CalcularPremio anteriormente existentes, de forma que tenham parâmetros diferentes da classe ancestral (linhas 11 e 12). Percebe-se também, que no nível mais alto dessa hierarquia, ou seja, na classe Funcionario, o método CalcularSalario é estático (linha 05) e o método CalcularPremio é virtual (linha 06).
Na classe descendente são utilizadas as diretivas overload e reintroduce, onde o método CalcularPremio possui a diretiva reintroduce (linha 12), visto que é um método virtual e deseja-se que a implementação desse método no seu ancestral seja ocultada, ou seja, não disponível nessa classe nem em seus descendentes.