Menu

Classes abstratas em Java: a palavra-chave abstract explicada

O que é uma classe abstrata em Java, como declarar métodos abstratos, por que você não pode instanciá-la e quando escolher uma classe abstrata em vez de uma interface.

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

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 Savings quanto Checking estendem Account, herdando o campo balance e a lógica de deposit. Uma classe estende apenas uma.
  • Interface - use-a para uma capacidade que classes não relacionadas podem compartilhar. Um Bird e um Airplane podem ambos ser Flyable sem 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.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR