Classes Abstratas vs Interfaces
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 38 de 87.
Agora que você entende tanto classes abstratas quanto interfaces, surge uma pergunta comum: quando você deve usar uma em vez da outra? A escolha depende do que você está tentando modelar.
Use uma classe abstrata quando as classes compartilham uma base comum com estado ou comportamento compartilhado. Classes abstratas podem ter variáveis de instância, construtores e uma mistura de métodos abstratos e concretos:
public abstract class Animal {
protected String name; // estado compartilhado
public Animal(String name) {
this.name = name;
}
public void sleep() { // comportamento compartilhado
System.out.println(name + " is sleeping");
}
public abstract void makeSound(); // deve ser implementado
}Use uma interface quando quiser definir uma capacidade que classes não relacionadas possam compartilhar. As interfaces focam no que um objeto pode fazer, não no que ele é:
public interface Flyable {
void fly();
}
// Classes não relacionadas podem compartilhar essa capacidade
class Bird extends Animal implements Flyable { ... }
class Airplane implements Flyable { ... }
class Drone implements Flyable { ... }Aqui está uma comparação rápida:
| Recurso | Classe Abstrata | Interface |
|---|---|---|
| Variáveis de instância | Sim | Apenas constantes |
| Construtores | Sim | Não |
| Herança múltipla | Não (single extends) | Sim (multiple implements) |
| Modificadores de acesso | Qualquer | Apenas público (para métodos abstratos) |
Uma diretriz prática: se você se encontrar criando uma classe abstrata com apenas métodos abstratos e sem estado, uma interface é provavelmente a melhor escolha.
Desafio
FácilVamos construir um sistema de veículos que demonstra quando usar classes abstratas versus interfaces. Você modelará veículos que compartilham estado e comportamento comuns por meio de uma classe abstrata, enquanto adiciona capacidades opcionais por meio de interfaces.
Você organizará seu código em cinco arquivos:
Vehicle.java: Crie uma classe abstrata que sirva como base para todos os veículos. Todo veículo possui um campobrand(String) e um campoyear(int). Inclua um construtor para inicializar ambos os campos, métodos getter para cada um e um método abstratostartEngine()que retorna uma String. Adicione também um método concretogetInfo()que retorna:[brand] ([year]). Este é um caso de uso perfeito para uma classe abstrata — os veículos compartilham estado (brand, year) e algum comportamento (getInfo), mas cada um liga seu motor de forma diferente.Convertible.java: Defina uma interface para veículos que podem converter seu teto. Essa capacidade não está ligada ao que um veículo é — é algo que certos veículos podem fazer. Declare dois métodos:openRoof()ecloseRoof(), ambos retornando String.Car.java: Crie uma classe que estendaVehiclee implementeConvertible. Um Car tem um campo adicionalnumDoors(int). Usesuperpara inicializar os campos herdados. ImplementestartEngine()para retornar:[brand] car engine started. ImplementeopenRoof()para retornar:[brand] roof openingecloseRoof()para retornar:[brand] roof closing.Motorcycle.java: Crie uma classe que estendaVehicle, mas NÃO implementeConvertible— motocicletas não têm teto! Uma Motorcycle tem um campohasSidecar(boolean). ImplementestartEngine()para retornar:[brand] motorcycle engine roaring.Main.java: Reúna tudo para mostrar a diferença entre classes abstratas e interfaces. Você receberá quatro entradas: uma marca de carro, um ano de carro, uma marca de motocicleta e um ano de motocicleta.Crie um Car (com 4 portas) e uma Motorcycle (sem sidecar). Primeiro, demonstre o comportamento compartilhado da classe abstrata imprimindo
getInfo()para ambos os veículos — ambos herdam isso de Vehicle. Em seguida, imprima o resultado destartEngine()para cada um — observe como cada tipo de veículo implementa isso de forma diferente.Finalmente, demonstre a capacidade da interface: como apenas o Car implementa
Convertible, chame e imprimaopenRoof()seguido decloseRoof()apenas no carro.
Você receberá quatro entradas: marca do carro (String), ano do carro (int), marca da motocicleta (String) e ano da motocicleta (int).
Sua saída deve mostrar seis linhas no total. Observe como ambos os veículos compartilham o estado da classe abstrata e o método getInfo(), mas apenas o Car tem a capacidade conversível — isso ilustra quando usar cada abordagem!
Folha de consulta
Use uma classe abstrata quando as classes compartilham uma base comum com estado ou comportamento compartilhado. Classes abstratas podem ter variáveis de instância, construtores e uma mistura de métodos abstratos e concretos:
public abstract class Animal {
protected String name; // estado compartilhado
public Animal(String name) {
this.name = name;
}
public void sleep() { // comportamento compartilhado
System.out.println(name + " is sleeping");
}
public abstract void makeSound(); // deve ser implementado
}Use uma interface quando quiser definir uma capacidade que classes não relacionadas podem compartilhar. Interfaces focam no que um objeto pode fazer, não no que ele é:
public interface Flyable {
void fly();
}
// Classes não relacionadas podem compartilhar esta capacidade
class Bird extends Animal implements Flyable { ... }
class Airplane implements Flyable { ... }
class Drone implements Flyable { ... }| Recurso | Classe Abstrata | Interface |
|---|---|---|
| Variáveis de instância | Sim | Apenas constantes |
| Construtores | Sim | Não |
| Herança múltipla | Não (extends único) | Sim (múltiplos implements) |
| Modificadores de acesso | Qualquer um | Apenas público (para métodos abstratos) |
Diretriz: Se você estiver criando uma classe abstrata com apenas métodos abstratos e sem estado, uma interface provavelmente é a melhor escolha.
Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Ler as entradas
String carBrand = scanner.nextLine();
int carYear = scanner.nextInt();
scanner.nextLine(); // consumir a quebra de linha
String motorcycleBrand = scanner.nextLine();
int motorcycleYear = scanner.nextInt();
// TODO: Criar um Car com 4 portas
// TODO: Criar uma Motorcycle sem sidecar (false)
// TODO: Imprimir getInfo() para ambos os veículos (demonstra o comportamento compartilhado da classe abstrata)
// TODO: Imprimir startEngine() para ambos os veículos (demonstra diferentes implementações)
// TODO: Imprimir openRoof() e closeRoof() apenas para o carro (demonstra a capacidade da interface)
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosCampos (Atributos)Método ConstrutorSobrecarga de ConstrutorRecapitulação - Calculadora Simples4Herança
Herança Básica (extends)A Palavra-chave superSobrescrita de Método (@Override)Encadeamento de ConstrutoresA Classe ObjectHerança Simples e MultinívelPor que não há Herança Múltipla de ClassesRecapitulação - Hierarquia de Funcionários7Métodos Especiais e a Classe Object
Método toString()equals() e hashCode()Método clone()compareTo() e ComparableInterface ComparatorRecapitulação - Ordenação Personalizada2Modificadores de Acesso e Encapsulamento
Visão Geral dos Níveis de AcessoMétodos Getter e SetterOcultação de InformaçõesA Palavra-chave finalRecapitulação - Gerenciador de Conta Bancária5Polimorfismo
Fundamentos de Sobrecarga de MétodosSobrescrita de Métodos (Tempo de Execução)Upcasting e DowncastingO Operador instanceofClasses e Métodos AbstratosRecapitulação - Calculadora de Formas8Conceitos Avançados de OOP
Composição vs HerançaAgregação vs ComposiçãoClasses Internas, Aninhadas e AnônimasEnums e Métodos de EnumRecords (Java 16+)Classes Seladas (Java 17+)11Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão BuilderPadrão ObserverPadrão Strategy3Propriedades de Classe e Membros Estáticos
Variáveis de Instância vs EstáticasMétodos EstáticosBlocos EstáticosConstantes (static final)Recapitulação - Contador e Utilitário6Interfaces e Classes Abstratas
Introdução às InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesDefault e Static em InterfacesClasses Abstratas vs InterfacesInterfaces FuncionaisRecapitulação - Sistema de Pagamento9Generics
Introdução a GenericsClasses GenéricasMétodos GenéricosParâmetros de Tipos DelimitadosWildcards (?, extends, super)Recapitulação - Container Genérico12Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Iterator