W3docs

Classes Abstratas em Java

Defina implementações parciais em Java com classes e métodos abstratos que as subclasses devem completar.

Uma classe abstrata é uma classe que não pode ser instanciada diretamente. Ela existe para ser estendida. Pode combinar métodos concretos (com corpo) e métodos abstratos (sem corpo, que a subclasse precisa implementar) — essa combinação é o que a diferencia tanto de uma classe comum quanto de uma interface.

Esta página explica como declarar uma classe abstrata, como as subclasses a completam, por que classes abstratas podem armazenar estado, o padrão de método template e como escolher entre uma classe abstrata e uma interface.

Use uma classe abstrata quando subclasses concretas precisam compartilhar estado e infraestrutura, não apenas um contrato de API. Se você precisa apenas de um contrato, uma interface é a escolha mais adequada. Classes abstratas são construídas sobre herança e polimorfismo, então é útil estar confortável com esses conceitos antes.

Declarando uma

Adicione abstract ao cabeçalho da classe. Adicione abstract a qualquer método que não tenha corpo:

public abstract class Shape {
  protected final String name;
  protected Shape(String name) { this.name = name; }

  public abstract double area();             // no body — subclass must provide one

  public String describe() {                  // concrete — inherited as-is
    return name + " area=" + area();
  }
}

Algumas consequências disso:

  • new Shape("circle") é um erro de compilação — classes abstratas não podem ser instanciadas.
  • Uma subclasse que não implementa todos os métodos abstratos herdados deve, ela mesma, ser declarada abstract. É possível ter subclasses abstratas de classes abstratas.
  • Uma classe abstrata pode ter um construtor — as subclasses o chamam com super(...) assim como um pai comum.

Implementando os métodos abstratos

Uma subclasse concreta deve fornecer um corpo para cada método abstrato herdado:

public class Circle extends Shape {
  private final double r;
  public Circle(double r) {
    super("circle");
    this.r = r;
  }
  @Override
  public double area() { return Math.PI * r * r; }
}

Agora new Circle(2) funciona, e describe() (herdado de Shape) chama o area() da subclasse via despacho dinâmico.

Classes abstratas podem armazenar estado

Essa é a principal razão para escolher uma classe abstrata em vez de uma interface. O pai pode declarar campos, escrever um construtor que os inicializa e oferecer métodos que operam sobre esse estado compartilhado:

public abstract class HttpHandler {
  private final String path;
  protected HttpHandler(String path) { this.path = path; }

  public final String path() { return path; }

  public abstract Response handle(Request r);     // subclass-specific behavior
}

Todo handler concreto tem um path; o pai o armazena e o expõe; cada subclasse escreve apenas a lógica por requisição. Interfaces não conseguem fazer isso por conta própria (elas não têm campos de instância).

Combinando métodos abstratos e concretos — o método template

Um padrão comum: a classe abstrata implementa o fluxo geral como um método concreto e deixa os pontos de variação como abstratos. As subclasses preenchem apenas as partes que diferem:

public abstract class Beverage {
  // Template — the algorithm, written once.
  public final void prepare() {
    boilWater();
    brew();              // varies
    pourIntoCup();
    addCondiments();     // varies
  }

  protected abstract void brew();
  protected abstract void addCondiments();

  private void boilWater()    { System.out.println("boiling water"); }
  private void pourIntoCup()  { System.out.println("pouring into cup"); }
}

public class Tea extends Beverage {
  protected void brew()           { System.out.println("steeping tea"); }
  protected void addCondiments()  { System.out.println("adding lemon"); }
}

Tea.prepare() executa o template do pai, que chama de volta os métodos brew e addCondiments de Tea via polimorfismo. Adicionar uma subclasse Coffee requer apenas os dois métodos abstratos.

Esse é o padrão de método template e é o motivo mais comum para escolher uma classe abstrata.

Abstrato vs final

abstract e final são opostos e o compilador impõe isso:

  • abstract class — deve ser subclassificada.
  • final class — não pode ser subclassificada.

O mesmo vale para métodos: métodos abstract devem ser sobrescritos; métodos final não podem ser. Usar os dois ao mesmo tempo é um erro de compilação.

Abstrato vs interface

Classe abstrataInterface
ConstrutoresSimNão
Campos de instânciaSimNão (apenas constantes public static final)
Corpos de métodosSim (qualquer número)Sim via default (usado com parcimônia)
HerançaSimples — apenas um paiMúltipla — várias interfaces
Usar quandoSubclasses compartilham estado e infraestruturaSubclasses compartilham apenas um contrato de API

Uma regra prática: comece com uma interface. Evolua para (ou adicione) uma classe abstrata somente se perceber que código compartilhado está se acumulando entre implementações. Os métodos default do Java 8+ em interfaces absorveram parte do território que as classes abstratas costumavam dominar, mas o caso de uso de "estado mutável compartilhado" ainda pertence ao domínio das classes abstratas.

Métodos abstratos não podem ser private ou static

  • private tornaria o método invisível para as subclasses — elas não poderiam sobrescrevê-lo, então a abstração seria inaplicável.
  • Métodos static não são despachados dinamicamente — não podem ser sobrescritos, apenas ocultados — portanto, um método estático abstrato seria sem sentido.

Essas duas combinações são erros de compilação.

Erros comuns

  • Esquecer abstract na classe. Um método sem corpo dentro de uma classe não abstrata não compila — o compilador exige que a classe envolvente também seja abstract.
  • Tentar instanciá-la. new Shape("x") é rejeitado em tempo de compilação. Instancie uma subclasse concreta.
  • Deixar um método não implementado em uma subclasse concreta. Se ao menos um método abstrato herdado não tiver corpo, a subclasse também deve ser declarada abstract.
  • Esperar herança múltipla. Uma classe pode estender apenas um pai (abstrato ou não). Se precisar combinar vários contratos, recorra a interfaces.

Um exemplo completo

java— editable, runs on the server

O que vem a seguir

Você viu agora a versão de abstração que vem com estado e código compartilhado. A versão que dispensa ambos — contrato puro, sem implementação — é a interface. Continue para interfaces em Java.

Prática

Prática
Uma classe estende uma classe abstrata, mas não implementa todos os seus métodos abstratos. O que acontece?
Uma classe estende uma classe abstrata, mas não implementa todos os seus métodos abstratos. O que acontece?
Was this page helpful?