Polimorfismo em Java Manoel Afonso Pereira de Lima Filho 10088000901 POO UFPa – Novembro – 2011 Tópicos ● Introdução ● Exemplo de polimorfismo ● Herança ● Classes abstratas ● Referências polimórficas ● Contrato ● Interface ● Considerações finais Introdução ● ● A herança é só o começo do polimorfismo. O polimorfismo fornecerá mais flexibilidade, clareza e facilidade de manutenção do código. ● O principal personagem é a interface. ● Permite a “programação no geral”. Instanciando um objeto ● 1- Especifique o tipo: Cao ● 2- Dê um nome a variável: Cao fido ● 3- Aloque espaço ao objeto: Cao fido new Cao(); ● 4- Conecte a instância à variável: Cao fido = new Cao(); Tipos de polimorfismo ● ● ● Polimorfismo de Inclusão: Animal an = new Gato(); Polimorfismo Paramétrico: List<Animal> a = new ArrayList<Animal>(); Polimorfismo de sobrecarga: public int soma(int a, int b); public int soma(int[] nums); Exemplo de Polimorfismo ● ● ● ● Suponha que as classes Cao, Gato e Passaro estendem a classe Animal. Suponha que Animal tenha um método chamado barulho(). O polimorfismo ocorre quando o programa invoca um método por uma variável da superclasse e a versão correta da subclasse é chamada. Assim, cada animal emitirá o seu som peculiar. Herança ● ● Um dos benefícios da herança é que podemos evitar códigos duplicados os agrupando em uma única classe. Podemos sobrescrever os métodos que devem ser mais específicos. Árvore de herança Árvore de herança ● ● É permitido: Gato meuGato = new Gato(); Animal meuCao = new Cao(); Faz sentido instanciar um animal? Animal anim = new Animal(); Árvore de herança ● ● Como Animal não é específica o suficiente, ela não deve ser instanciada. Assim, basta torná-la abstrata: abstract class Animal { … } Classes abstratas ● ● Não é possível instanciá-las, é um erro de compilação. Exemplo: Animal a = new Animal(); //Errado Podemos instanciar apenas as suas classes concretas. Animal b = new Gato(); //Correto Classes abstratas ● ● ● Definem apenas parte da implementação, isto é, métodos que ainda podem ser úteis para as subclasses. Se um método não for genérico o bastante, então o torne abstrato: public abstract void mover(); Métodos abstratos não têm corpo e se uma classe possuir algum método abstrato, ela deverá ser abstrata também. Por que usar abstract? ● ● Não faz sentido uma classe abstrata possuir todos os métodos implementados porque esse código genérico não será útil para as subclasses. Abstract apenas define o modelo do objeto. Por que usar abstract? ● ● ● Poderemos, então, usar um tipo da superclasse como argumento, tipo de retorno ou tipo de matriz de um método. Será fácil criar novos subtipos sem ter que reescrever ou adicionar novos métodos para lidar com esses novos tipos. Protocolo de um método abstrato: “Todos os subtipos desse tipo têm ESSE método.” Por que usar abstract? ● Sem um modelo, teríamos de fazer isso para um método fazer os animais emitirem sons: public void somAnimal ( Cachorro c ) {…} Por que usar abstract? ● Sem um modelo, teríamos de fazer isso para um método fazer os animais emitirem sons: public void somAnimal ( Cachorro c ) {…} public void somAnimal ( Gato g) {…} Por que usar abstract? ● ● Sem um modelo, teríamos de fazer isso para um método fazer os animais emitirem sons: public void somAnimal ( Cachorro c ) {…} public void somAnimal ( Gato g) {…} public void somAnimal ( Passaro p) {…} E quanto mais classes surgirem, mais sobrecarga haverá! Por que usar abstract? ● Com um modelo, faríamos apenas isso: public void somAnimal ( Animal c ) {…} ● Pois todos os animais emitem som, cada um a seu modo. Classes abstratas ● ● ● Lembre-se que um método abstrato não tem corpo. Você deverá dar um na primeira subclasse concreta. Por exemplo, a classe concreta Passaro deve implementar todos os métodos abstratos de Animal. Referências polimórficas ● ● ● Ocorre quando uma superclasse “aponta” para um dos seus subtipos. Object pets[] = new Object[3]; pets[0] = new Cao(); pets[1] = new Gato(); pets[2] = new Passaro(); Vantagem: Um mesmo array armazena vários tipos. Desvantagem: quais métodos pets[1] tem? Referências polimórficas Apenas métodos de Object! Referências polimórficas ● Para usarmos os métodos específicos, temos que converter o objeto para o seu tipo mais específico. Assim: Gato gt = (Gato) pets[1]; nos dará uma referência ao “gato que está dentro do objeto”. Referências polimórficas ● Se fizermos Gato gt = (Gato) pets[0]; teremos uma referência a um Gato? Referências polimórficas ● Se fizermos Gato gt = (Gato) pets[0]; teremos uma referência a um Gato? ● ● Não! Pois pets[0] foi instanciado como um Cao. Isso irá gerar em tempo de execução uma exceção ClassCastException. Referências polimórficas ● Caso você não tenha certeza do tipo, use a palavra reservada instanceof. Contrato ● ● ● As classes Cao, Gato e Passaro são subclasses de Object. A classe Object não define nenhum contrato com as nossas classes. Por isso temos que saber de antemão quem é quem antes de fazer a conversão. Interface ● ● ● Interfaces são classes 100% abstratas. A interface irá definir o contrato entre as classes, isto é, o comportamento mínimo que elas possuem. Assim, você define funcionalidades que outras classes podem obter, independentemente da sua árvore de herança. Interface ● A interface Animal: Interface ● Uma classe concreta (Cao). Interface Interface ● Com um contrato feito, vamos fazer um array polimórfico: Interface ● Cada posição do array já é de fato um Cao, um Gato e um Passaro. Interface ● Cada posição do array já é de fato um Cao, um Gato e um Passaro. Cada chamada é executada de acordo com o tipo real! Interface ● Outro exemplo: Interface ● É garantido que o retorno é uma classe concreta de Animal. Interface ● ● Até agora, vimos como interfaces ajudam a tornar o programa mais simples. Vamos ver como elas podem tornar o programa flexível. Interface ● ● ● ● Temos a interface Animal e suas três subclasses. Todas com um contrato feito: animais emitem som e se movem. Suponha que queiramos agora que apenas os cães sejam capazes de correr em uma competição. Isso quer dizer que temos de mudar o contrato. Interface ● ● ● Não podemos adicionar um método correr() na classe Animal pois afetaria a todos. Poderíamos adicionar o método diretamente na classe Cao! Mas perderíamos qualquer relação entre um cão corredor e um humano corredor (atleta). Interface ● Veja o diagrama de classes para pessoas: Interface ● Ora, se um cão corredor e um atleta tem algo em comum, as duas classes poderiam estender uma classe chamada Corredor. Interface Interface Interface ● Herança múltipla é proibido em Java! ● Usaremos uma interface. Interface Interface ● ● Agora criamos uma “conexão” entre duas classes de árvores de herança distintas. Podemos pensar nisso como “adicionar a funcionalidade de correr” ao cão e ao atleta. Interface ● Assim, podemos criar um método que faça com que os que correm, corram. public void fazerCorrer(Corredor c) {…} ● Não importa de qual árvore de herança os objetos pertencem, pois sabemos que os que vierem poderão correr. Quando usar ● ● ● Crie uma classe que não estenda nada (exceto Object) quando esta não passar no teste do É-UM. Cria uma subclasse para ter uma versão mais específica de uma classe. Crie uma classe abstrata para ter um modelo para suas subclasses, com algum código implementado. Quando usar ● ● Crie uma interface para adicionar funcionalidades a uma classe. Assim, qualquer classe pode usar um interface, independentemente de sua árvore de herança. Conclusão ● ● ● Classes abstratas fornecem reuso de código, mas são genéricas demais para serem instanciadas. Interfaces são classes 100% abstratas e criam o contrato com todos os seus subtipos. As interfaces fornecem uma ampla flexibilidade para uso em tipos de retorno, parâmetro ou de matriz.