Java Básico
Herança e Polimorfismo
Fábio Gondim
Herança
Herança
• Um dos possíveis mecanismos de reutilização de código / funcionalidade.
• Juntamente com Interfaces são a fonte para o polimorfismo (conforme explicado posteriormente). • Uma nova classe (subclasse) é criada estendendo uma
outra preexistente que passa a ser a sua superclasse.
public class Mamifero extends Animal { //... }
“Mamifero” é uma subclassede “Animal”. “Animal” é uma superclassede “Mamifero”. • As subclasses herdam da superclasse todos os campos e
métodos públicos (public) e protegidos (protected) mesmo que as classes estejam em diferentes pacotes.
Herança
Neste exemplo, simplificado, objetos do tipo Animal (Animal animal;) são capazes de dormir.
Objetos do tipo Mamífero (Mamífero mamifero;) são capazes de dormir e mamar.
Objetos do tipo Cachorro (Cachorro cachorro;) são capazes de dormir, mamar e latir.
Herança
Neste exemplo, simplificado, objetos do tipo Animal(Animal animal;) são capazes de dormir.
Objetos do tipo Mamífero(Mamífero mamifero;) são capazes de dormire mamar.
Objetos do tipo Cachorro(Cachorro cachorro;) são capazes de dormir, mamare latir.
Herança
Um Mamífero é um Mamífero Um Mamífero é um Animal Um Cachorro é um Cachorro Um Cahorro é um Mamífero Um Cachorro é um Animal Um Animal é um AnimalHerança
Não confunda declaração de tipo com instanciação.
Animal cachorro1 = new Cachorro;
// Para a JVM cachorro1 referencia um Animal. // Apenas as operações de um Animal (e Object) // estarão naturalmente disponíveis.
Mamífero cachorro2 = new Cachorro;
// Para a JVM cachorro2 referencia um Mamífero. // As operações de Mamífero e Animal estarão // naturalmente disponíveis.
Cachorro cachorro3 = new Cachorro;
// Para a JVM cachorro3 referencia um Cachorro. // As operações de Animal, Mamífero e Cachorro // estarão naturalmente disponíveis.
• Campos e métodos sem modificadores de visibilidade (package-private) só serão visíveis pelas subclasses se estiverem dentro do mesmo pacote.
• Campos e métodos privados não são acessíveis pelas subclasses. O acesso aos campos privados podem ocorrer por intermédio de métodos de acesso que sejam visíveis nas subclasses.
• Em UML este relacionamento é chamado de
Generalização. Ao subir na hierarquia caminha-se para a Generalização enquanto que ao descer caminha-se para a Especialização.
Herança
?
?
?
?
?
?
?
?
?
?
?
?
G
e
n
e
ra
li
za
çã
o
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
E
sp
e
ci
a
li
za
çã
o
Exemplo
public classConta {
privateString numero;
private doublesaldo;
privateCliente titular;
publicConta(String numero, doublesaldo, Cliente titular) { //...
}
public voidcreditar(doublevalor) { //...
}
public voiddebitar(doublevalor) throwsSaldoNegativoException { //...
}
public voidtransferir(doublevalor, Conta destino) throws
SaldoNegativoException { //...
} //... }
Suponha uma classe Conta que representa uma conta bancária.
Exemplo
(continuação)public class Poupanca extends Conta {
public Poupanca(String numero, double saldo, Cliente
titular) {
super(numero, saldo, titular);
}
public void renderJuros(double taxa) {
doublesaldo = this.getSaldo();
this.creditar(saldo * taxa); }
}
Uma conta poupança é muito similar a uma conta corrente normal, mas pode render juros. Solução: faça Poupanca estender Conta e a especialize acrescentando a operação renderJuros(double taxa).
• Classes declaradas como final não podem ser
estendidas (especializadas).
public final class PostoCombustível { // Esta classe não pode ser estendida // ...
}
Herança
Herança Múltipla
C++
C++
C++
C++
Java
• Ao contrário de C++, Java não suporta herança múltipla: Uma classe qualquer, C, não pode estender ao mesmo tempo duas outras classes quaisquer, A e B.
Herança Múltipla
Java
C++
C++
C++
C++
Obs.: Java permite implementar múltiplas interfaces (assunto visto adiante).
“(...) Herança múltipla é útil quando você quer que uma nova classe combine vários contratos e herde parte de, ou toda a, implementação destes contratos. Mas quando existe mais de uma superclasse, surgem problemas quando um comportamento da superclasse é herdado por dois caminhos. Suponha, por um momento, a seguinte árvore de tipos:
Herança Simples versus Herança Múltipla
“(...) Isto é comumente chamado de herança diamante, e não existe nada de errado com ela. Muitos projetos legítimos mostram esta estrutura. Os problemas existem na herança de implementação, quando a
implementação de W guarda algum estado.
Herança Simples versus Herança Múltipla
Se a classe W tivesse, por exemplo, um campo público denominado goggin, e se você tivesse uma referência a um objeto do tipo Z chamado zref, a que se referiria zref.goggin? Ela poderia se referir à cópia de X de goggin, ou poderia se referir à cópia de Y, ou X e Y poderiam compartilhar uma única cópia de goggin porque Z é na
realidade somente um W mesmo que ele seja também um X e um Y. Resolver tais problemas não é trivial e complicam o projeto e o uso da hierarquia de classes. Para evitar tais questões, a linguagem de programação Java usa o modelo da herança simples da programação orientada a objetos. Herança simples exclui alguns projetos úteis e corretos. Os problemas da herança múltipla surgem da herança múltipla de implementação, mas em muitos casos a herança múltipla é usada para herdar diversos contratos abstratos e talvez uma implementação concreta. Fornecer um meio de herdar um contrato abstrato sem herdar uma implementação permite os benefícios da herança múltipla sem o problema da herança múltipla de implementação. A herança de um contrato abstrato é denominado herança de interface.(...)
[GOSLING et al, 2007, p. 125, 126]
• Toda classe estende Object, direta ou indiretamente. Por este motivo alguns métodos estão disponíveis em todas as classes: clone(), equals(Object obj), finalize(), getClass(), hashCode(), notify(), notifyAll(), toString(), etc.
Herança - Classe Object
• Há um forte acoplamento entre uma classe e as suas subclasses. Mudanças nas superclasses quase sempre provocam efeitos colaterais em suas subclasses. Cuidado com os chamados efeitos “dominó” ou “gelatina”. • É um relacionamento estático (definido durante o
processo de compilação) e portanto não pode mudar durante a execução do sistema.
• A agregação/composição, explicada posteriormente é um relacionamento dinâmico e portanto mais flexível do que a herança.
• Mais adiante veremos que o uso de interfaces deve ser encorajado.
Herança
• Se a superclasse tem um construtor parametrizado e não possui um construtor padrão (sem parâmetros), as suas subclasses terão que providenciar em seus construtores uma chamada ao construtor parametrizado da superclasse.
Subclasses e construtores
public classPoupanca extendsConta {
publicPoupanca(String numero, doublesaldo, Cliente titular) { super(numero, saldo, titular);
}
public voidrenderJuros(doubletaxa) { doublesaldo = this.getSaldo(); this.creditar(saldo * taxa); }
}
• Toda classe possui pelo menos um construtor. • Um construtor padrão sem parâmetros é incluído
automaticamente pelo compilador sempre que não houver nenhum construtor definido.
• Se um construtor qualquer for especificado, o compilador não incluirá mais nenhum outro.
• Um construtor de uma subclasse deve, obrigatoriamente, invocar o construtor de sua superclasse. Se não houver uma chamada ao construtor da superclasse o compilador incluirá uma chamada para um construtor padrão sem parâmetros por intermédio da instrução super(). Se não existir um construtor padrão na superclasse ocorrerá um erro durante a compilação. O erro desaparecerá se for incluído um construtor padrão na superclasse ou se for inserida, manualmente, uma chamada ao construtor parametrizado da superclasse: super(Tipo1 param1, ...). Esta chamada deve ser a primeira instrução a ser executada.
Subclasses e construtores
Sobrescrita (Override)
public abstract classPassaro { public voidfazerNiho() {
System.out.println(“Fazendo ninho com palha,
gravetos, etc...");
}
// Outros métodos }
A maioria dos
pássaros faz ninhos
assim
Sobrescrita (Override)
Public classJoaoDeBarro extendsPassaro { public voidfazerNiho() {
System.out.println(“Fazendo ninho com argila ...");
}
// Outros métodos }
Mas alguns são
especiais e fogem à
regra
Sobrescrita (Override)
Public classPinguimextendsPassaro { public voidfazerNiho() {
System.out.println(“Fazendo ninho com pedregulhos ...");
}
// Outros métodos }
Mas alguns são
especiais e fogem à
regra
• Muitas vezes, nem todas as operações definidas em uma superclasse interessam a todas as suas subclasses. Neste caso é necessário que a subclasse que deseja alterar o comportamento sobrescreva o método herdado mantendo a sua assinatura e modificando o seu interior.
Sobrescrita (Override)
public abstract classPassaro { public voidfazerNiho() {
System.out.println(“Fazendo ninho com palha e
gravetos...");
}
// Outros métodos }
public classJoãoDeBarro extendsPassaro { public voidfazerNinho() {
System.out.println(“Fazendo ninho com argila...");
} }
• Ao sobrescrever um método não é permitido reduzir a sua visibilidade. O contrário é permitido, ou seja, pode-se aumentar a visibilidade de um método, mas nunca diminuí-la.
Sobrescrita (Override)
public abstract classPassaro { public voidfazerNiho() {
System.out.println(“Fazendo ninho com palha e
gravetos...");
}
// Outros métodos }
public classJoãoDeBarroextendsPassaro {
protectedvoidfazerNinho() { //ERRO
System.out.println(“Fazendo ninho com argila...");
} }
• Não confunda sobrescrita com sobrecarga. Sobrecarga envolve assinaturas diferentes enquanto que sobrescrita não. Sobrescrita sempre envolve herança enquanto que sobrecarga nem sempre.
Sobrescrita x Sobrecarga
public abstract classPassaro { public voidvoar() {
// implementação }
public voidvoar(doublealtitude) {
// implementação }
public voidvoar(doublealtitude, doublevelocidade) {
// implementação }
}
No exemplo acima voar é um método sobrecarregado.
• Não há sobrecarga e causa um erro de compilação:
– Mudar apenas o tipo de retorno:
Sobrescrita x Sobrecarga
public abstract classPassaro { public voidvoar() {
// implementação }
public doublevoar() {
// implementação } }
Erro:
Método voar()
duplicado
• Não há sobrecarga e causa um erro de compilação:
– Mudar apenas o identificador de um ou mais parâmetros:
Sobrescrita x Sobrecarga
public abstract classPassaro { public voidvoar(doublealtitude) {
// implementação }
public doublevoar(doublealt) {
// implementação }
}
Erro:
Sobrescrita x Sobrecarga
Sobrescrita Sobrecarga Sobrecarga
Classes Abstratas
• O que você faria se alguém lhe pedisse para
desenhar um animal sem ser específico?
– Perguntaria que animal?
– Escolheria arbitrariamente um animal? – Desenharia um animal imaginário?
– Desenharia um animal composto de partes de vários tipos de animal?
– Desenharia um monstro? – Ficaria paralizado?
Classes Abstratas
Classes Abstratas
Animal animal = new Animal();
// Que Animal???!!!
// Alce? ... Canguru? ... Elefante? ... // Leão? ... Girafa? ... Tartaruga? ...
// Tigre? ... Tubarão? ...
Classes Abstratas
• Algumas classes são importantes para fatorarcomportamento comum, mas não devem gerar instâncias porque são conceitualmente muito abstratas e não possuem elementos suficientes para isoladamente definir objetos.
Animal animal; animal = new Animal();
Classes Abstratas
• “Uma característica extremamente útil daprogramação orientada a objetos é o conceito de classe abstrata. Usando classes abstratas você pode declarar classes que definem somente parte de uma implementação, deixando para as classes
estendidas fornecer implementações específicas de alguns ou todos os métodos. O oposto de abstrato é concreto – uma classe que possui somente métodos concretos, incluindo implementações de quaisquer métodos abstratos herdados de superclasses, é uma classe concreta.”
Classes Abstratas
• Em Java, uma classe abstrata é declarada utilizando-se o modificador abstract antes da palavra class.
public abstract class Animal { //...
}
• Em UML uma classe abstrata tem o seu nome grafado em itálico.
Classes Abstratas
• Uma classe abstrata
não
pode ser instanciada.
Animal animal = new Animal();
public abstract class Animal { //...
}
Classes Abstratas
• Uma classe abstrata pode conter tanto
métodos concretos como métodos abstratos:
public abstract class Animal {public void dormir() {
// ...
}
public abstract void emitirSom();
// ...
}
Itálico
Classes Abstratas
• Uma classe concreta não pode conter
métodos abstratos.
• Uma classe concreta que estende uma classe
abstrata é obrigada a implementar os
métodos abstratos de sua superclasse
abstrata.
• Uma classe abstrata pode estender uma outra
que seja abstrata. Neste caso é opcional
implementar ou não os métodos abstratos.
Polimorfismo
voar() voar() voar() voar() voar() voar() voar() voar() voar() voar()• Aonde é esperado um objeto de uma determinada classe pode ser utilizado qualquer outro objeto cujo tipo seja de uma de suas subclasses diretas ou indiretas.
Princípio da Substituição
public static void soar(Animal animal) {
animal.emitirSom(); //op. polimórfica
}
• O princípio da substituição também é válido para tipo de retorno.
publicAnimal gereAnimal(bytetipo) { switch(tipo) {
case1:
return newCachorro(); case2:
return new Cavalo(); case3:
return new Porco(); default:
return new Gato();
} }
Pode ser retornado um objeto de qualquer um destes tipos
• Também pode ser utilizado na ligação entre a referência e a instância:
Princípio da Substituição
Animal cachorro, cavalo, gato, porco; cachorro = new Cachorro();
cavalo = new Cavalo();
gato = new Gato();
porco = new Porco();
Note que no exemplo abaixo as referências são do tipo Animal enquanto que os objetos são subtipos da classe Animal.
• Quando o método a ser executado é definido durante a compilação do programa, ocorre o mecanismo de ligação prematura (early binding). • Para a utilização de polimorfismo, a linguagem de programação orientada a objetos deve suportar o conceito de ligação tardia (late binding), onde a definição do método que será efetivamente invocado só ocorre em tempo de execução. O mecanismo de ligação tardia também é conhecido pelos termos dynamic binding ou run-time binding.
Ligação Tardia e Polimorfismo
• A operação emitirSom() é abstrata na classe Animal o que significa que todas as subclasses concretas desta classe abstrata precisam implementá-la. Cada uma delas implementará a operação ao seu modo e teremos, então diversas formas de emissão de som.
public static void soar(Animal animal) {
animal.emitirSom(); //op. polimórfica
}
Ligação Tardia e Polimorfismo
• A definição de qual emitirSom() será utilizado depende de quem a variável animal referencia no momento. Isto só será definido durante a execução do programa e poderá variar. A operação
animal.emitirSom() é uma operação polimórfica.
public static void soar(Animal animal) {
animal.emitirSom(); //op. polimórfica
}
Ligação Tardia e Polimorfismo
• Uma interface define as operações de um tipo sem especificar as suas implementações. Todos os métodos de uma interface são qualificados como public e abstract automaticamente.
public interfaceRepositorioContasIF {
public abstract void inserir(Conta c);
public abstract Conta procurar(String numero) throws
ObjetoNaoEncontradoException;
public abstract boolean existe(String numero);
public abstract void atualizar(Conta c);
}
// A cláusula throws será explicada posteriormente.
Interfaces
• Todos os métodos de uma interface são qualificados como public e abstract automaticamente e, portanto, o código neste slide é equivalente ao do slide anterior.
public interfaceRepositorioContasIF {
voidinserir(Conta c);
Conta procurar(String numero) throws
ObjetoNaoEncontradoException;
boolean existe(String numero);
voidatualizar(Conta c); }
// A cláusula throws será explicada posteriormente.
Interfaces
• Uma interface só pode conter campos definidos como constantes e que, portanto, precisam ser inicializados. Estes campos são qualificados automaticamente como public, static e final. Os códigos abaixo são
equivalentes.
public interface Voador {
public static final int i= 1;
public abstract void voar();
}
public interface Voador {
int i = 1;
void voar(); }
Interfaces
• Uma classe pode implementar várias interfaces: public classClasseCD implementsInterfaceC, InterfaceD {
public voidmetodoC() {
// Implementar método de InterfaceC...
}
public voidmetodoD() {
// Implementar método de InterfaceD...
} }
• Neste exemplo a classe ClasseCD está obrigada a implementar os métodos das duas interfaces.
Interfaces
• Interfaces não podem conter construtores. • Interfaces podem estender outras interfaces.
public interface InterfaceA {
void metodoA(); }
// Em um outro arquivo
public interface InterfaceB extends InterfaceA {
void metodoB(); }
• A classe que implementar a interface InterfaceB (acima) deverá definir concretamente os métodos abstratos das duas interfaces (InterfaceA e InterfaceB).
Interfaces
• Classes abstratas que implementam interfaces podem postergar a implementação dos métodos abstratos herdados para as suas subclasses concretas.
• Use interfaces sempre que quiser definir operações que algumas classes possam realizar independente de onde elas estejam na árvore de herança.
– Ex.:
• Um morcego é um mamífero que é capaz de voar. • Uma pipa é um brinquedo que é capaz de voar.
• Crie uma interface Voador (por exemplo). Faça com que a classe Morcego estenda Mamífero e implemente Voador. Faça com que a classe Pipa estenda brinquedo e implemente Voador. Aonde for esperado um Voador poderá ser utilizado tanto uma Pipa como um Morcego.
Interfaces
• public: elementos públicos são acessíveis globalmente (de qualquer parte);
• private: classes internas, construtores, atributos e métodos privados são acessíveis somente na própria classe (inclusive nas classes internas a ela);
• protected: elementos protegidos são acessíveis no mesmo pacote e em subclasses (neste caso mesmo não estando no mesmo pacote);
• Sem nenhum modificador: Visível por outros elementos do mesmo pacote. São conhecidos como default ou package-private (termo utilizado pela Sun para designar a visibilidade quando não é utilizado public, protected ou private).
• O modificador “public” pode ser aplicado a classes, enumerações, interfaces, construtores, métodos e atributos; • A mesma regra acima vale para a visibilidade padrão (default)
“package-private” adotada quando há omissão;
• O modificador “protected” não pode ser aplicado a classes, enumerações e interfaces. Pode ser utilizado em construtores, métodos e atributos;
• A mesma regra acima é válida para o modificador “private”; • Variáveis locais não podem receber atributos de visibilidade.
Elas sempre são visíveis apenas no bloco aonde foram criadas.
Modificadores de Visibilidade
• Quanto a visibilidade, classes externas e
interfaces podem ser ou públicas ou
package-private. Classes e interfaces package-private
só são visíveis dentro do mesmo pacote. Em
alguns editores deve ser marcado a opção
“default” para que a classe seja criada sem
modificador de visibilidade.
• Interfaces devem conter apenas assinaturas
de métodos públicos.
Modificadores de Visibilidade
Modificadores de Visibilidade
• O termo “membro” refere-se a métodos e
atributos de classes e instâncias.
• Um membro não privado é visível por
qualquer classe dentro do mesmo pacote.
• Apenas membros públicos podem ser
visualizados por classes que estejam em
pacotes diferentes e que não sejam
subclasses. No caso de subclasses os membros
protegidos da superclasse também serão
visíveis.
Modificadores de Visibilidade
Visibilidade (S = Sim, N = Não)
Modificador (em Alpha) Package One PackageTwo Alpha Beta AlphaSub Gamma
public S S S S
protected S S S N
sem modificador S S N N
private S N N N
A tabela a seguir mostra quando os membros (atributos e métodos) da classe Alpha são visíveis nas classes do esquema acima, para cada um dos modificadores que podem ser aplicados a eles.
Esquema adaptado de tutorial da Sun: