Default e Static em Interfaces
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 37 de 87.
Originalmente, as interfaces podiam conter apenas métodos abstratos sem implementação. A partir do Java 8, as interfaces ganharam a capacidade de incluir métodos default e métodos estáticos, tornando-as mais flexíveis.
Um método default fornece uma implementação diretamente na interface usando a palavra-chave default. Classes que implementam a interface herdam esse comportamento automaticamente, mas podem sobrescrevê-lo se necessário:
public interface Drawable {
void draw();
default void display() {
System.out.println("Displaying on screen...");
draw();
}
}Qualquer classe que implemente Drawable recebe o método display() gratuitamente. Isso é particularmente útil para adicionar novos métodos a interfaces existentes sem quebrar as classes que já as implementam.
Métodos estáticos em interfaces funcionam como métodos estáticos em classes. Eles pertencem à própria interface e são chamados usando o nome da interface:
public interface MathOperations {
static int add(int a, int b) {
return a + b;
}
}
// Uso
int result = MathOperations.add(5, 3);Métodos estáticos de interface não podem ser sobrescritos e não são herdados pelas classes que as implementam. Eles são úteis para métodos utilitários relacionados ao propósito da interface.
Se uma classe implementa duas interfaces com a mesma assinatura de método default, a classe deve sobrescrever esse método para resolver o conflito.
Desafio
FácilVamos construir um sistema de mensagens que demonstre métodos default e estáticos em interfaces. Você criará uma interface com funcionalidade integrada que as classes implementadoras podem usar diretamente ou personalizar conforme necessário.
Você organizará seu código em quatro arquivos:
Messenger.java: Defina uma interface que represente qualquer serviço de mensagens. Ela deve declarar um método abstratosendMessage(String message)que retorna void. Adicione um método default chamadosendWithTimestamp(String message)que imprime:[TIMESTAMP]seguido pela chamada desendMessage(message). Também inclua um método estático chamadoformatMessage(String message)que retorna a mensagem convertida para letras maiúsculas.EmailMessenger.java: Crie uma classe que implementeMessenger. Ela possui um camporecipient(String). Inclua um construtor para inicializar o destinatário. ImplementesendMessage()para imprimir:Email to [recipient]: [message]. Esta classe usa o método defaultsendWithTimestamp()como ele é.SMSMessenger.java: Crie outra classe que implementeMessenger. Ela possui um campophoneNumber(String). Inclua um construtor para inicializar o número de telefone. ImplementesendMessage()para imprimir:SMS to [phoneNumber]: [message]. Sobrescreva o método defaultsendWithTimestamp()para imprimir:[URGENT]seguido pela chamada desendMessage(message)em vez de usar o comportamento de timestamp padrão.Main.java: Reúna tudo para demonstrar os métodos de interface default e estáticos. Você receberá três entradas: um destinatário de e-mail, um número de telefone e uma mensagem. Primeiro, use o método estáticoMessenger.formatMessage()para formatar a mensagem e armazená-la. Em seguida, crie um EmailMessenger e um SMSMessenger. Para cada mensageiro, chamesendWithTimestamp()com a mensagem formatada — observe como o EmailMessenger usa a implementação default enquanto o SMSMessenger usa sua versão sobrescrita.
Você receberá três entradas: o destinatário do e-mail (String), o número de telefone (String) e a mensagem (String).
Sua saída deve mostrar quatro linhas no total — duas para cada mensageiro. O EmailMessenger mostrará o prefixo de timestamp padrão, enquanto o SMSMessenger mostrará seu prefixo urgente personalizado. Ambos usarão a mensagem formatada estaticamente (em maiúsculas)!
Folha de consulta
A partir do Java 8, as interfaces podem incluir métodos default e métodos estáticos, além de métodos abstratos.
Métodos default fornecem uma implementação diretamente na interface usando a palavra-chave default. As classes que implementam a interface herdam esse comportamento automaticamente, mas podem sobrescrevê-lo se necessário:
public interface Drawable {
void draw();
default void display() {
System.out.println("Displaying on screen...");
draw();
}
}Métodos estáticos em interfaces pertencem à própria interface e são chamados usando o nome da interface. Eles não podem ser sobrescritos e não são herdados pelas classes implementadoras:
public interface MathOperations {
static int add(int a, int b) {
return a + b;
}
}
// Usage
int result = MathOperations.add(5, 3);Se uma classe implementa duas interfaces com a mesma assinatura de método default, a classe deve sobrescrever esse método para resolver o conflito.
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 emailRecipient = scanner.nextLine();
String phoneNumber = scanner.nextLine();
String message = scanner.nextLine();
// TODO: Use Messenger.formatMessage() para formatar a mensagem e armazená-la
// TODO: Crie um EmailMessenger com o destinatário do e-mail
// TODO: Crie um SMSMessenger com o número de telefone
// TODO: Chame sendWithTimestamp() no EmailMessenger com a mensagem formatada
// TODO: Chame sendWithTimestamp() no SMSMessenger com a mensagem formatada
}
}
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