1. Sintaxe de uma classe abstrata
Vamos entender o que é uma classe abstrata em Java e por que ela é necessária. Imagine que você tem um conjunto LEGO: você tem um conjunto de peças (por exemplo, “rodas”, “blocos”) e há peças especiais para modelos específicos. Uma classe abstrata é como uma instrução genérica de como lidar com as peças sem descrever a montagem de um modelo específico: os blocos se conectam por encaixes, as rodas são presas aos eixos, as peças podem ser empilhadas em camadas. A classe abstrata descreve exatamente essas regras gerais e etapas obrigatórias, mas não detalha a montagem de um modelo concreto. Os modelos concretos são as subclasses: elas seguem a instrução geral e acrescentam as etapas que faltam.
Em Java, uma classe abstrata é uma classe que não pode ser instanciada diretamente (não é possível fazer new AbstractClass()), mas que pode ser estendida para implementar seus métodos “inacabados”.
Classes abstratas são úteis quando:
- Um grupo de objetos tem comportamento ou estado em comum, mas parte da lógica é diferente.
- Você quer implementar parte da funcionalidade “por padrão” e deixar outra parte para os filhos.
- Você quer impedir a criação direta de objetos dessa classe (por exemplo, “Animal” por si só não existe, mas “Gato”, sim).
Como declarar uma classe abstrata
É simples: antes da palavra class, escreva a palavra-chave abstract.
public abstract class Animal {
// Campos (por exemplo, o nome do animal)
protected String name;
// Construtor
public Animal(String name) {
this.name = name;
}
// Método abstrato — apenas declaração, sem implementação!
public abstract void makeSound();
// Método comum (com implementação)
public void sleep() {
System.out.println(name + " dorme: Zzz...");
}
}
Particularidades:
- Uma classe abstrata pode conter tanto métodos implementados quanto abstratos.
- Uma classe abstrata pode conter campos, construtores e até métodos static.
- Não é possível criar um objeto de uma classe abstrata diretamente:
-
Animal a = new Animal("Alguém"); // Erro!
Como declarar um método abstrato
Um método abstrato é um método sem corpo, ou seja, sem chaves e sem código dentro. Ele é declarado com a palavra-chave abstract e deve terminar com ponto e vírgula ;.
public abstract void makeSound();
- Métodos abstratos só podem ser declarados dentro de uma classe abstrata.
- Uma classe que herda de uma classe abstrata é obrigada a implementar todos os métodos abstratos; caso contrário, ela também deve ser abstract.
2. Herança de classes abstratas: como funciona
Vamos ver um exemplo concreto. Suponha que temos uma classe abstrata Animal com um método abstrato makeSound(). Agora vamos criar a classe filha Dog, que implementa esse método:
public class Dog extends Animal {
public Dog(String name) {
super(name); // Chamada do construtor da classe base
}
@Override
public void makeSound() {
System.out.println(name + " late: Au-au!");
}
}
E mais uma classe filha:
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " mia: Miau!");
}
}
Agora podemos usar essas classes no programa:
public class Main {
public static void main(String[] args) {
Animal dog = new Dog("Sharik");
Animal cat = new Cat("Murka");
dog.makeSound(); // Sharik late: Au-au!
cat.makeSound(); // Murka mia: Miau!
dog.sleep(); // Sharik dorme: Zzz...
cat.sleep(); // Murka dorme: Zzz...
}
}
Atenção:
Podemos declarar variáveis do tipo Animal, mas só podemos instanciar objetos de subclasses concretas (não abstratas).
Esquema: como fica
. Animal (abstract)
/ \
Dog Cat
(implementa makeSound) (implementa makeSound)
3. Quando usar classe abstrata e quando usar interface?
Essa é uma das perguntas mais comuns em entrevistas — e com razão! Vamos entender:
- Classe abstrata — quando os objetos têm estado (por exemplo, campos) em comum, lógica em comum (métodos com implementação) e você quer fornecer um “esqueleto” de comportamento com possibilidade de complementação.
- Interface — quando você quer definir apenas um conjunto de métodos (contrato), sem implementação e sem estado. Desde o Java 8, interfaces possuem métodos default/static, mas ainda assim interface trata do “o que o objeto deve fazer”, não do “como ele faz”.
Exemplo do cotidiano:
“Ave” — classe abstrata: todas as aves têm bico, asas e podem voar (mas de formas diferentes).
“Voador” — interface: não só as aves voam, mas também aviões e super-heróis! Todos voam de maneiras diferentes, mas o importante é: eles conseguem voar.
4. Exemplos práticos
Exemplo 1: classe abstrata com implementação parcial
Suponha que você está fazendo um jogo com diferentes tipos de transporte. Todos podem se mover, mas fazem isso de formas diferentes. Porém, todos têm velocidade, nome e uma forma padrão de parar.
public abstract class Transport {
protected String name;
protected int speed;
public Transport(String name, int speed) {
this.name = name;
this.speed = speed;
}
// Método abstrato — implementação nas subclasses
public abstract void move();
// Método implementado
public void stop() {
System.out.println(name + " parou.");
}
}
public class Car extends Transport {
public Car(String name, int speed) {
super(name, speed);
}
@Override
public void move() {
System.out.println(name + " anda pela estrada a " + speed + " km/h.");
}
}
public class Bicycle extends Transport {
public Bicycle(String name, int speed) {
super(name, speed);
}
@Override
public void move() {
System.out.println(name + " pedala a " + speed + " km/h.");
}
}
Uso:
Transport car = new Car("Toyota", 120);
Transport bike = new Bicycle("Stels", 25);
car.move(); // Toyota anda pela estrada a 120 km/h.
bike.move(); // Stels pedala a 25 km/h.
car.stop(); // Toyota parou.
bike.stop(); // Stels parou.
Exemplo 2: método abstrato com parâmetro
public abstract class Shape {
public abstract double area();
}
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
public class Rectangle extends Shape {
private double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
Uso:
Shape c = new Circle(3);
Shape r = new Rectangle(4, 5);
System.out.println("Área do círculo: " + c.area());
System.out.println("Área do retângulo: " + r.area());
5. Particularidades e nuances de classes abstratas
- Uma classe abstrata pode usar quaisquer modificadores de acesso: public, protected, private (por exemplo, para campos).
- É possível declarar uma classe abstrata sem métodos abstratos. Isso pode ser útil se você apenas quiser impedir a criação de instâncias da classe base.
- Se uma classe herda de uma classe abstrata, mas não implementa todos os métodos abstratos, ela também deve ser declarada como abstract.
- Uma classe abstrata pode ter construtores. Eles são chamados ao criar um objeto da subclasse (via super(...)).
- Métodos abstratos não podem ser private (caso contrário, não será possível implementá-los na subclasse).
- Classes abstratas podem conter métodos e campos static.
- Uma classe abstrata pode implementar interfaces, mas não é obrigada a implementar seus métodos — isso pode ser feito pelas subclasses.
Tabela: comparação entre classe abstrata e interface
| Classe abstrata | Interface | |
|---|---|---|
| Palavra-chave | |
|
| Pode conter campos | Sim (quaisquer) | Desde o Java 8 — apenas static/final |
| Métodos com implementação | Sim | Desde o Java 8 — default/static |
| Métodos abstratos | Sim | Sim |
| Herança múltipla | Não | Sim (é possível implementar várias interfaces) |
| Construtores | Sim | Não |
| Pode criar objeto | Não | Não |
6. Erros comuns ao trabalhar com classes e métodos abstratos
Erro nº 1: tentativa de instanciar uma classe abstrata.
Se você escrever new Animal("Alguém"), o compilador lembrará imediatamente que classes abstratas não se destinam a isso. Lembre-se: abstração é um “esqueleto”, não um “organismo vivo”.
Erro nº 2: esqueceu de implementar todos os métodos abstratos na subclasse.
Se sua classe não implementar pelo menos um método abstrato da classe base, ela própria deve ser declarada como abstract; caso contrário, você terá um erro de compilação.
Erro nº 3: declarou um método abstrato fora de uma classe abstrata.
Em Java, não é permitido declarar um método abstrato em uma classe comum (não abstrata) — o compilador reclamará imediatamente.
Erro nº 4: tentar tornar um método abstrato private ou static.
Métodos abstratos não podem ser private (caso contrário, não podem ser sobrescritos) e não podem ser static (porque métodos estáticos não são sobrescritos). E também não podem ser final, porque final impede a sobrescrita.
Erro nº 5: esqueceu o modificador de acesso do método abstrato.
Se você não especificar explicitamente um modificador, o método terá visibilidade de pacote (package-private), o que às vezes não é o que você pretendia.
GO TO FULL VERSION