O que é uma classe abstrata
Uma interface declara comportamento sem estado. Uma classe comum é totalmente implementada e pode ser instanciada. Uma classe abstrata fica entre as duas: pode carregar campos, construtores e métodos concluídos como uma classe normal, mas também pode deixar alguns métodos sem implementação e proíbe ser instanciada diretamente. Você a marca com a palavra-chave abstract.
A ideia é reunir em um só lugar tudo o que as subclasses têm em comum, ao mesmo tempo em que obriga cada subclasse a preencher as partes que realmente diferem.
Animal define getName() uma única vez para todas as subclasses e declara sound() como abstract - um método com assinatura, mas sem corpo, que Dog deve fornecer.
Você não pode instanciar uma classe abstrata
Como uma classe abstrata pode ter métodos não concluídos, criá-la diretamente deixaria você com um objeto incompleto. O compilador rejeita isso:
Animal a = new Animal("???"); // error: Animal is abstract; cannot be instantiated
Você sempre instancia uma subclasse concreta - uma que tenha implementado todos os métodos abstratos. Essa instância da subclasse pode então ser mantida em uma variável do tipo abstrato, que é exatamente como você usa a abstração.
Métodos abstratos obrigam as subclasses a decidir
Um método abstract é uma promessa que a subclasse precisa cumprir. Se uma subclasse esquecer de implementar um deles, a própria subclasse se torna abstrata e o compilador avisa você. Essa é a principal alavanca da classe abstrata: ela garante que certo comportamento existe sem ditar o que ele faz.
describe() é escrito uma única vez em Shape e, ainda assim, chama o area() próprio de cada subclasse. A classe abstrata fornece a estrutura comum; as subclasses fornecem os detalhes.
Estado compartilhado e construtores
Diferentemente de uma interface tradicional, uma classe abstrata pode ter campos de instância e definir construtores. O construtor nunca cria um Animal ou um Shape sozinho - ele é executado via super(...) quando uma subclasse é criada, inicializando o estado compartilhado.
O campo balance, deposit e applyInterest ficam em um único lugar. Apenas a política que de fato varia - interestRate() - é deixada abstrata. O construtor de uma subclasse deve chamar super(...) para inicializar esse estado herdado.
Uma pegadinha: misturar abstract e final
abstract e final são opostos. Um método abstract exige uma sobrescrita; um método final a proíbe. Marcar a mesma coisa das duas formas - ou tornar uma classe abstrata final - é um erro de compilação. Lembre-se também de que uma classe abstrata pode ter zero métodos abstratos: declarar uma classe abstract apenas para impedir que ela seja instanciada é legal e às vezes útil para tipos base que você só quer estender.
abstract final class Bad { } // error: abstract and final conflict
abstract class Base {
abstract final void f(); // error: an abstract method can't be final
}
Classe abstrata versus interface
Elas se sobrepõem, então a escolha depende do que você precisa compartilhar:
- Classe abstrata - use-a para classes fortemente relacionadas que compartilham estado e código. Tanto
SavingsquantoCheckingestendemAccount, herdando o campobalancee a lógica dedeposit. Uma classe estende apenas uma. - Interface - use-a para uma capacidade que classes não relacionadas podem compartilhar. Um
Birde umAirplanepodem ambos serFlyablesem compartilhar nenhuma implementação. Uma classe pode implementar várias.
Um padrão frequente combina as duas: uma interface define o contrato e uma classe abstrata implementa o código repetitivo para que as subclasses concretas preencham somente o que é único.
A seguir: Polimorfismo
Repare que em todos os exemplos acima guardamos uma instância de subclasse em uma variável do tipo abstrato e, ao chamar um método, obtivemos automaticamente o comportamento da subclasse. Essa única capacidade - um tipo de referência, muitos comportamentos em tempo de execução - é o polimorfismo, e é o que faz as classes abstratas e as interfaces valerem a pena. Esse é o tema da próxima página.
Perguntas frequentes
O que é uma classe abstrata em Java?
Uma classe abstrata é uma classe declarada com a palavra-chave abstract que não pode ser instanciada por si só. Ela existe para ser estendida. Pode combinar métodos e campos totalmente implementados (estado e código compartilhados para as subclasses) com métodos abstract que não têm corpo - estes passam a ser responsabilidade de cada subclasse implementar.
É possível instanciar uma classe abstrata em Java?
Não. new AbstractType() é um erro de compilação porque uma classe abstrata pode ter métodos não implementados (abstract), de modo que o objeto ficaria incompleto. Você instancia uma subclasse concreta que implementa todos os métodos abstratos e depois a armazena em uma variável do tipo abstrato.
Qual é a diferença entre uma classe abstrata e uma interface em Java?
Uma classe abstrata pode ter campos de instância, construtores e lógica parcialmente implementada, mas uma classe só pode estender uma. Uma interface declara comportamento sem estado de instância, e uma classe pode implementar várias. Use uma classe abstrata para compartilhar estado e código entre subclasses fortemente relacionadas; use uma interface para dar a classes não relacionadas uma capacidade comum.