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 compartilharem uma base comum com estado ou comportamento compartilhado. Classes abstratas podem ter variáveis de instância, construtores e uma combinação 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 se concentram 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 { ... }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 (um único extends) | Sim (vários implements) |
| Modificadores de acesso | Qualquer um | Apenas públicos (para métodos abstratos) |
Uma diretriz prática: se você se encontrar criando uma classe abstrata apenas com métodos abstratos e sem estado, uma interface provavelmente será a melhor escolha.
Desafio
FácilVamos criar um sistema de veículos que demonstra quando usar classes abstratas em vez de 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 tem um campobrand(String) e um campoyear(int). Inclua um construtor para inicializar ambos os campos, métodos getter para cada um deles e um método abstratostartEngine()que retorne uma String. Adicione também um método concretogetInfo()que retorne:[brand] ([year]). Este é um caso de uso perfeito para uma classe abstrata: os veículos compartilham estado (brand, year) e parte do comportamento (getInfo), mas cada um inicia o motor de uma maneira diferente.Convertible.java: defina uma interface para veículos que possam converter o teto. Essa capacidade não está ligada ao que um veículo é: é algo que determinados 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 demonstrar a diferença entre classes abstratas e interfaces. Você receberá quatro entradas: a marca de um carro, o ano de um carro, a marca de uma motocicleta e o ano de uma 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 maneira diferente.Por fim, demonstre a capacidade da interface: como apenas o Car implementa
Convertible, chame e imprimaopenRoof()seguido decloseRoof()somente para o 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 ao todo. 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!
Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Ler entradas
String carBrand = scanner.nextLine();
int carYear = scanner.nextInt();
scanner.nextLine(); // consumir nova linha
String motorcycleBrand = scanner.nextLine();
int motorcycleYear = scanner.nextInt();
// TODO: Crie um Car com 4 portas
// TODO: Crie uma Motorcycle sem sidecar (false)
// TODO: Imprima getInfo() para ambos os veículos (demonstra o comportamento compartilhado da classe abstrata)
// TODO: Imprima startEngine() para ambos os veículos (demonstra implementações diferentes)
// TODO: Imprima 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 IteratorPratique por conta própria: Compilador de Java online