1. Classe abstrata: revisando o básico
Antes de partir para a comparação, vamos relembrar o que é uma classe abstrata.
Classe abstrata — é uma classe que não pode ser instanciada diretamente (não dá para escrever new Animal()), mas pode conter tanto métodos comuns (com implementação) quanto métodos abstratos (sem implementação). Uma classe abstrata é frequentemente usada como base para outras classes, que herdam seu comportamento e/ou são obrigadas a implementar alguns métodos.
Por exemplo, se no nosso aplicativo há diferentes tipos de transporte, podemos criar uma classe abstrata Transport:
public abstract class Transport {
private String model;
public Transport(String model) {
this.model = model;
}
public String getModel() {
return model;
}
// Método abstrato — sem implementação, apenas declaração
public abstract void move();
// Método comum — possui implementação
public void printInfo() {
System.out.println("Modelo do transporte: " + model);
}
}
Características da classe abstrata:
- Pode conter campos (estado).
- Pode conter métodos implementados.
- Pode conter métodos abstratos (obrigatórios de implementar nas subclasses).
- Não é possível instanciar diretamente.
- Usada para comportamento e estado comuns.
2. Interface: revisando o básico
Interface — é um conjunto de métodos que uma classe deve implementar. A interface não descreve estado (não pode ter campos comuns, apenas constantes) e não contém implementações de métodos (até o Java 8). Interface é um contrato puro: “Se você me implementa, é obrigado a saber fazer isto”.
Exemplo de interface:
public interface Movable {
void move(int x, int y);
}
Características da interface:
- Não contém estado (apenas constantes public static final).
- Até o Java 8 — apenas métodos abstratos (a partir do Java 8 surgiram métodos default e static, mas falaremos deles depois).
- Os métodos são sempre public abstract por padrão.
- Uma classe pode implementar várias interfaces.
- Usada para descrever capacidades, “o que a classe sabe fazer”.
3. Tabela comparativa: classe abstrata vs. interface
Hora de comparar essas duas ferramentas lado a lado! Aqui está uma tabela ilustrativa:
| Característica | Classe abstrata | Interface |
|---|---|---|
| Sintaxe | |
|
| É possível criar instâncias? | Não | Não |
| Pode conter métodos comuns? | Sim | Até o Java 8 — não; a partir do Java 8 — apenas default/static |
| Pode conter métodos abstratos? | Sim | Sim (todos os métodos até o Java 8 são abstratos) |
| Pode conter campos (estado)? | Sim (quaisquer campos) | Apenas public static final (constantes) |
| Pode conter construtores? | Sim | Não |
| Herança | Apenas uma classe (abstrata ou concreta) | Pode implementar várias interfaces |
| Modificadores de métodos | Qualquer um (public, protected, private) | Por padrão, os métodos são public abstract. A partir do Java 9 é possível adicionar métodos private para uso interno na interface |
| Herança por meio de | |
|
| Uso típico | Implementação e estado comuns | Descrição de capacidades e papéis |
| Exemplos da JDK | |
|
4. Quando usar interface e quando usar classe abstrata?
Interface use quando:
- Você quer descrever “o que a classe sabe fazer”, sem se preocupar em como ela faz.
- Você precisa que a classe possa implementar várias capacidades independentes.
- Exemplo: Comparable (pode ser comparado), Serializable (pode ser serializado), Runnable (pode ser executado em uma thread).
Classe abstrata use quando:
- Você quer definir implementação e estado comuns que todas as subclasses terão.
- Você precisa que todas as subclasses tenham determinados campos ou métodos já implementados.
- A herança é estritamente “um-para-um”: a classe pode herdar de apenas uma classe (concreta ou abstrata).
Analogias do dia a dia
- Interface — é como uma “carteira de motorista”: se você a tem, você pode dirigir um carro, mas ninguém diz em qual carro exatamente nem como você faz isso.
- Classe abstrata — é como um “projeto genérico de um automóvel”: todos os carros têm volante, pedais, motor, mas cada marca implementa os detalhes à sua maneira.
5. Exemplos da biblioteca padrão do Java
Interface: Comparable
public interface Comparable<T> {
int compareTo(T o);
}
Qualquer classe que implemente essa interface é obrigada a implementar o método compareTo. Por exemplo, String, Integer, LocalDate e muitas outras.
Classe abstrata: AbstractList
public abstract class AbstractList<E> implements List<E> {
// Implementação de alguns métodos de List por padrão
// Alguns métodos são deixados como abstratos
}
AbstractList já implementa parte do comportamento das coleções (por exemplo, métodos de adição/remoção), mas deixa alguns métodos abstratos para que as subclasses possam implementá-los à sua maneira.
6. Exemplos de código: comparação na prática
Interface
Vamos criar uma interface e uma classe que a implementa.
public interface Printable {
void print();
}
public class Document implements Printable {
@Override
public void print() {
System.out.println("Imprimindo documento...");
}
}
Classe abstrata
Agora, uma classe abstrata e sua subclasse.
public abstract class Machine {
public void turnOn() {
System.out.println("Máquina ligada.");
}
public abstract void work();
}
public class Printer extends Machine {
@Override
public void work() {
System.out.println("A impressora está imprimindo...");
}
}
Classe que combina ambos: interface e classe abstrata
public class SmartPrinter extends Machine implements Printable {
@Override
public void work() {
System.out.println("A impressora inteligente está funcionando...");
}
@Override
public void print() {
System.out.println("A impressora inteligente está imprimindo...");
}
}
7. Implementação múltipla de interfaces: por que isso é bacana
Em Java, uma classe pode herdar de apenas uma classe (abstrata ou concreta), mas pode implementar quantas interfaces quiser! Isso permite criar arquiteturas flexíveis e extensíveis.
public interface Scannable {
void scan();
}
public class MultiFunctionPrinter extends Machine implements Printable, Scannable {
@Override
public void work() {
System.out.println("O multifuncional está funcionando...");
}
@Override
public void print() {
System.out.println("O multifuncional está imprimindo...");
}
@Override
public void scan() {
System.out.println("O multifuncional está escaneando...");
}
}
Quando escolher cada um?
- Se você está projetando funcionalidade base com estado comum (por exemplo, campos), use uma classe abstrata.
- Se você quer adicionar “rótulos de capacidades” aos objetos (por exemplo, “sabe imprimir”, “sabe comparar”, “sabe serializar”) — use interfaces.
- Se não tiver certeza — comece com interface. Em Java isso é considerado uma boa prática: interfaces oferecem mais flexibilidade e extensibilidade.
8. Erros comuns e armadilhas
Erro nº 1: Você tenta herdar de várias classes — o Java não permite!
Uma classe pode herdar de apenas uma classe, mas implementar muitas interfaces. Por exemplo, class A extends B, C — erro, mas class A extends B implements X, Y, Z — tudo bem.
Erro nº 2: Confundir campos de interface e de classe.
Em uma interface só é possível declarar constantes (public static final). Não é possível declarar estado comum, por exemplo, private int count; — o compilador vai impedir na hora.
Erro nº 3: Não implementar todos os métodos da interface.
Se a classe não implementar nem que seja um único método da interface — ela deve ser declarada como abstract; caso contrário, o compilador emitirá um erro.
Erro nº 4: Tentar instanciar uma interface ou classe abstrata.
Ambos esses tipos — “semiprodutos”. Eles podem apenas ser estendidos, não instanciados diretamente:
Printable p = new Printable(); // Erro!
Machine m = new Machine(); // Erro!
Erro nº 5: Achar que uma interface pode ter construtor.
Interfaces não podem ter construtores, pois não descrevem o estado de objetos. Apenas classes (concretas e abstratas).
GO TO FULL VERSION