Menu

Herança em Java: extends, super e sobrescrita

Como uma subclasse em Java herda campos e métodos com extends, chama a classe pai por meio de super e sobrescreve comportamentos - além das armadilhas mais comuns.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Herança é reutilização do tipo "é-um"

A herança permite que uma nova classe seja construída sobre uma já existente. A nova classe -a subclasse- recebe automaticamente os campos e métodos da superclasse e, em seguida, adiciona ou altera o que for necessário. Você recorre a ela quando um tipo é uma forma mais específica de outro: um Dog é um Animal, uma SavingsAccount é uma BankAccount.

Você a escreve com a palavra-chave extends. Tudo que for public ou protected na classe pai fica disponível na classe filha sem precisar reescrever.

Dog nunca declara name nem eat(), mas mesmo assim tem ambos. É exatamente esse o objetivo: o comportamento compartilhado fica em um só lugar.

super: alcançando a classe pai

O construtor de uma subclasse precisa garantir que a classe pai seja inicializada primeiro. Você faz isso com super(...), que chama o construtor da classe pai e precisa ser a primeira instrução do construtor da subclasse. Se você o omitir, o Java insere uma chamada silenciosa ao construtor sem argumentos da classe pai - e, se a classe pai não tiver esse construtor, o código não compila.

A saída mostra que o construtor da classe pai é executado antes do corpo da classe filha - a construção flui do topo da hierarquia para baixo.

Sobrescrevendo métodos

Uma subclasse pode substituir um método herdado redefinindo-o com a mesma assinatura. Isso é a sobrescrita, e você deve sempre marcá-la com @Override. A anotação não é obrigatória, mas faz o compilador verificar se você realmente corresponde a um método da classe pai - ela detecta erros de digitação como tostring() em vez de toString(), que de outra forma criariam silenciosamente um método totalmente novo.

Mesmo que o array seja do tipo Animal, cada elemento executa o seu próprio speak(). O Java escolhe o método com base no objeto real em tempo de execução, não no tipo declarado da variável - essa é a base do polimorfismo.

Chamando a versão da classe pai com super.method()

Sobrescrever não precisa descartar o trabalho da classe pai. Use super.method() para executar a versão herdada e, em seguida, ampliá-la:

Sem o super., chamar log dentro de TimestampLogger.log chamaria a si mesmo e entraria em recursão infinita. super. significa explicitamente "a versão da classe pai".

Campos herdados e acesso

Uma subclasse enxerga os membros public e protected da classe pai, mas não os private. Os campos private ainda existem no objeto - os próprios métodos da classe pai podem acessá-los - mas a subclasse não pode referenciá-los diretamente. Use protected quando quiser que as subclasses tenham acesso, mantendo ao mesmo tempo um membro escondido de código não relacionado.

class Base {
    private int secret;      // invisível para as subclasses
    protected int shared;    // visível para as subclasses
}

class Derived extends Base {
    void demo() {
        shared = 5;          // OK
        // secret = 5;       // erro de compilação - private em Base
    }
}

É também por isso que o construtor de uma subclasse muitas vezes precisa chamar super(...): é a única forma de inicializar o estado privado da classe pai.

Impedindo a herança com final

Às vezes uma classe não deve poder ser estendida de jeito nenhum - String é final exatamente por esse motivo. Marcar uma classe como final proíbe criar subclasses; marcar um método como final proíbe sobrescrevê-lo, embora ainda permita estender a classe.

final class Constants { }            // não pode ter subclasses

class Config {
    final void load() { }            // as subclasses podem estender Config
                                     // mas não podem sobrescrever load()
}

Recorra a final quando o comportamento de uma classe precisar ser garantido e imutável em todo o programa - é um sinal deliberado de "não estenda", não a opção padrão.

Uma armadilha comum: prefira composição quando não é "é-um"

A herança é tentadora porque reutiliza código, mas acopla fortemente a classe filha à classe pai. Se a relação não for um genuíno "é-um" - digamos, um Car que por acaso precisa de um Engine - não faça Car extends Engine. Um carro tem um motor, ele não é um motor. Modele isso com um campo (composição) em vez disso:

class Car {
    private Engine engine = new Engine();   // Car TEM-UM Engine

    void start() { engine.ignite(); }
}

Use herança somente quando a subclasse for realmente uma forma especializada da superclasse e você quiser herdar e substituir o comportamento dela.

Próximo: Interfaces

A herança com extends lhe dá uma única classe pai e uma implementação compartilhada. Mas uma classe só pode estender uma classe - então como dar uma capacidade comum a classes não relacionadas? É para isso que existem as interfaces: um contrato que muitas classes podem implementar, e o tema da próxima página.

Perguntas frequentes

O que é herança em Java?

A herança permite que uma classe (a subclasse) reutilize os campos e métodos de outra classe (a superclasse) usando a palavra-chave extends. A subclasse recebe automaticamente os membros públicos e protegidos da classe pai e pode adicionar novos ou substituir o comportamento herdado por meio da sobrescrita. Ela modela uma relação "é-um": um Dog é um Animal.

O que a palavra-chave super faz em Java?

super se refere à classe pai. super(...) dentro de um construtor chama o construtor da classe pai (e precisa ser a primeira instrução), enquanto super.method() chama a versão da classe pai de um método que você sobrescreveu. Isso permite que a subclasse se apoie na lógica da classe pai em vez de substituí-la por completo.

Qual é a diferença entre sobrescrita e sobrecarga em Java?

A sobrescrita redefine um método herdado em uma subclasse usando a mesma assinatura, mudando o comportamento - marque-a com @Override. A sobrecarga define vários métodos com o mesmo nome, mas com listas de parâmetros diferentes, na mesma classe. A sobrescrita tem a ver com herança e despacho em tempo de execução; a sobrecarga são apenas dois métodos que por acaso compartilham um nome.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR