Polimorfismo, Interfaces, Classes Abstractas/Exemplos Simples de Herança em Java

From Wiki**3

< Polimorfismo, Interfaces, Classes Abstractas

Os seguintes exemplos estão escritos em Java.

Hierarquia de Classes

As seguintes classes descrevem uma hierarquia: o conceito mais abstracto é o de animal, seguido de mamífero, seguido de gato (o conceito mais específico).

  • Todas as classes definem o método nadar. Este método é definido em todas as classes pois admitiu-se que cada tipo de animal tem uma forma especial de nadar.
  • Apenas a classe Animal define o método irPara. Este método é definido como final pois admitiu-se que é suficientemente genérico para todos os animais.
  • Apenas a classe Gato define o método miar (a acção não é suficientemente geral para ser definida a um nível superior).

Class "Animal"

A definição da classe Animal é como se segue:

 class Animal {
   final void irPara(int p) {
     System.out.println("Animal: deslocação para " + p);
   }
   void nadar() {
     System.out.println("Animal: nadar");
   }
 }

Class "Mamífero"

A definição da classe Mamífero é como se segue:

 class Mamífero extends Animal {
   void nadar() {
     System.out.println("Mamífero: nadar");
   }
 }

Class "Gato"

A definição da classe Gato é como se segue:

 class Gato extends Mamífero {
   void nadar() {
     System.out.println("Gato: nadar");
   }
   void miar() {
     System.out.println("Gato: miar");
   }
 }

No exemplo seguinte demonstra-se um caso simples de polimorfismo assim como de binding. O exemplo de polimorfismo pode ser observado na iniciação da referência animal: em lugar de referenciar um objecto da classe Animal, referencia um objecto da classe Gato. Este procedimento é admissível para quaisquer referências e objectos, desde que o tipo associado à referência esteja ao mesmo nível hierárquico, ou acima, que o tipo associado ao objecto (i.e., seria admissível a uma referência do tipo Animal referenciar um objecto do tipo Mamífero ou uma do tipo Mamífero referenciar um objecto do tipo Gato).

O conceito de binding pode ser observado na selecção dos métodos irPara (early binding) e nadar (late binding).

A simple application

Test class

 public class Teste {
   public static void main(String[] args) {
     Animal animal = new Gato();
     Gato   gato   = new Gato();
     animal.irPara(22);       // irPara é final em Animal: early binding
     animal.nadar();          // nadar é seleccionado durante a execução: late binding
     //animal.miar();         // Erro de compilação: não há miar em Animal
     ((Gato)animal).miar();   // downcast
     gato.irPara(33);
     gato.nadar();
     gato.miar();
   }
 }

Result

O resultado é o seguinte:

 Animal: deslocação para 22
 Gato: nadar
 Gato: miar
 Animal: deslocação para 33
 Gato: nadar
 Gato: miar

Note-se que, no late binding, é o tipo do objecto e não o da referência que determina o método a invocar. No early binding, i.e., para métodos final (recorde-se que métodos private são implicitamente final) e static, é o tipo da referência que define o método a invocar (em tempo de compilação). Veja-se o exemplo seguinte.

Polymorphic Application

O aspecto da selecção do método a executar através de late binding é importante na medida em que contribui para a uniformização do código.

Test application

No exemplo seguinte, a invocação de nadar é efectuada uniformemente.

 class Teste {
   public static void main(String[] args) {
     Animal[] v = { new Animal(), new Mamífero(), new Gato() };
     for (Animal a: v) a.nadar();
   }
 }

Result

O resultado é o seguinte:

 Animal: nadar
 Mamífero: nadar
 Gato: nadar

Como se pode apreciar, o vector contém referências para animais não especificados. No entanto, a invocação do método nadar é feita de acordo com a classe do objecto.

Polymorphism and Downcasts

Test application

Considere-se agora a aplicação de downcasts:

 class Teste {
   public static void main(String[] args) {
     Animal[] v = { new Animal(), new Mamífero(), new Gato() };
     // v[0].miar();   // erro de compilação
     // v[1].miar();   // erro de compilação
     // v[2].miar();   // erro de compilação
     // ((Gato)v[0]).miar();   // excepção ClassCastException (erro durante a execução)
     // ((Gato)v[1]).miar();   // excepção ClassCastException (erro durante a execução)
     ((Gato)v[2]).miar();
   }
 }

Problems at execution time

A execução de downcasts inválidos, i.e., conversão para tipos incompatíveis (como se pode observar na linhas 7 e 8 do exemplo), implica a ocorrência de erros em tempo de execução.

Erro de execução, caso o primeiro cast fosse descomentado:

 Exception in thread "main" java.lang.ClassCastException: Animal
         at Teste.main(Teste.java:7)

Erro de execução, caso o segundo cast fosse descomentado:

 Exception in thread "main" java.lang.ClassCastException: Mamífero
         at Teste.main(Teste.java:8)

Result (without errors)

Resultado sem erros:

 Gato: miar