04 fevereiro 2009

SCJP - 02 - Polimorfismo


Em termos simples, polimorfismo é a habilidade de um tipo, A, aparecer e ser usado como outro tipo, B. Em linguagens fortemente tipadas, isso normalmente significa que o tipo A deriva, de alguma forma, do tipo B, ou o tipo A implementa alguma interface que representa o tipo B [Traduzido livremente da versão inglesa da Wikipedia]

Lendo a citação acima, e relembrando o post anterior (além de alguns outros conceitos base do java), começamos a pensar melhor sobre polimorfismo. Sabemos que em Java, TODOS os objetos herdam da classe Object. Com isso, todos os objetos em java (exceto os do tipo Object) passam no teste IS-A para o seu próprio tipo e para a classe Object, tornando-os polimórficos. Além disso, mesmo que duas classes não possuam superclasses em comum, mas implementem uma mesma interface, elas podem ser consideradas polimórficas (pois, da mesma forma, passam no teste IS-A para seu próprio tipo e para a referida interface).

É por isso que nós não nos assustamos quando vemos esse tipo de situação:
HashSet a = new HashSet();
Set b = a;
Collection c = a;
Object d = a;

Ignorando o "pra que serve um HashSet/Set/Collection?", note que o tipo de construção acima é perfeitamente possível, e a justificativa é dada na própria API Java:


Com isso, nós começamos a entender as cinco regras básicas sobre polimorfismo em java:

  • Uma variável de referência só pode ter um tipo. Uma vez declarada, o tipo da variável de referência não pode mais ser alterado (embora o objeto a quem ela referencia o possa).
  • Uma referência é uma variável, então ela pode ser reatribuída a outros objetos (a menos que seja declarada como final).
  • O tipo da variável de referência determina quais métodos podem ser invocados no objeto referenciado por ela.
  • Uma variável de referência pode referenciar qualquer objeto de mesmo tipo, ou qualquer objeto de qualquer subtipo do tipo declarado pela variável.
  • Se uma variável de referência é declarada como uma interface, ela pode referenciar qualquer objeto que implemente esta interface.

Mas que p**** é uma variável de referência? Pois bem. O código abaixo declara uma variável de referência.

String referencia = 
    new String("Eu sou uma referência");

O código abaixo não declara uma variável de referência.

int a = 1;
boolean ehReferencia = false;

Mesmo com toda essa "facilidade", é preciso tomar alguns cuidados. Você sempre pode acessar um objeto qualquer usando uma referência a um tipo mais geral (sua superclasse ou uma interface implementada pelo tipo do objeto em questão). Mas única coisa tratada em tempo de execução em relação ao objeto são seus métodos de instância. Não os métodos estaticos. Não as variáveis de instância (ou variável membro). Apenas os métodos de instância.

class Base {
    //declarado como publico só como ilustração
    public int inteiro;
    
    public Base(int inteiro) {
        this.inteiro = inteiro;
    }

    public int getInteiro() {
        return inteiro;
    }
    
    public void setInteiro(int inteiro) {
        this.inteiro = inteiro;
    }

    public static String quemSouEu() {
        return "classe Base";
    }
}

class Derivada extends Base {
    public int inteiro;
    
    public Derivada (int inteiro) {
        super(inteiro);
        setInteiro(inteiro);
    }

    public int getInteiro() {
        return inteiro;
    }
    
    public void setInteiro(int inteiro) {
        this.inteiro = inteiro;
    }

    public static String quemSouEu() {
        return "classe Derivada";
    }
}

public class Principal {
    public static void main (String[] args) {
        Derivada derivada = new Derivada(10);
        Base base = derivada;
        derivada.setInteiro(5);

        System.out.println(
            "Derivada.getInteiro() = " 
            + derivada.getInteiro());

        System.out.println(
            "Base.getInteiro() = " 
            + base.getInteiro());

        System.out.println(
            "Derivada.quemSouEu() = " 
            + derivada.quemSouEu());

        System.out.println(
            "Base.quemSouEu() = " 
            + base.quemSouEu());

        System.out.println("Derivada.inteiro = " 
            + derivada.inteiro);

        System.out.println(
            "Base.inteiro = " 
            + base.inteiro);    
    }
}

O código acima não vai tratar o inteiro da classe Base e o inteiro da classe Derivada como uma coisa só. Declarar a variável de instância inteiro novamente na classe Derivada não sobrescreve a que foi declarada na classe Base. Note o que acontece com os métodos estáticos também.


Outro exemplo relacionado às 5 leis. Veja o resultado da compilação logo após o código.

class Base {}

class Derivada extends Base {
    public String soTemNaDerivada() {
        return "teste de invocação de metodos";
    }
}

public class Principal {
    public static void main(String[] args) {
        Derivada derivada = new Derivada();
        Base base = derivada;

        base.soTemNaDerivada();
    }
}


O tipo da variável de referência define
que métodos podem ser invocados por ela

Creative Commons License
Esta obra está licenciada sob uma Licença Creative Commons.
Comentários
0 Comentários

0 comments:

Postar um comentário

Regras são chatas, mas...

- Seu comentário precisa ter relação com o assunto do post;
- Em hipótese alguma faça propaganda de outros blogs ou sites;
- Não inclua links desnecessários no conteúdo do seu comentário;
- Se quiser deixar sua URL, comente usando a opção OpenID;
- CAIXA ALTA, miguxês ou erros de ortografia não serão tolerados;
- Ofensas pessoais, ameaças e xingamentos não são permitidos;