CodeGym /Cursos /JAVA 25 SELF /Métodos default em interfaces

Métodos default em interfaces

JAVA 25 SELF
Nível 21 , Lição 2
Disponível

1. Introdução

Há muito tempo (antes do Java 8) a interface era muito rígida: nela era possível declarar apenas métodos abstratos (sem implementação) e constantes (public static final). Isso era conveniente até surgir um grande problema: o evoluir das bibliotecas.

Imagine a situação

Você desenvolveu uma biblioteca popular que tem uma interface:

public interface Movable {
    void move(int x, int y);
}

Milhares de programadores no mundo todo escrevem suas classes que implementam essa interface. Depois de alguns anos você percebe que todos sentem falta do método reset(), que devolve o objeto à posição inicial. Você adiciona na interface:

public interface Movable {
    void move(int x, int y);
    void reset();
}

E então começa o apocalipse: todos os projetos que usam sua interface param de compilar! Afinal, agora eles são obrigados a implementar o novo método, do qual ninguém sabia. A migração vira dor.

Métodos default — a solução!

O Java 8 apresentou os métodos default: agora é possível adicionar um método com implementação diretamente na interface! Todas as classes antigas recebem automaticamente uma implementação padrão, e seu código não quebra. E, se quiser, você pode sobrescrever o método do seu jeito.

2. Sintaxe de métodos default

Um método default é um método comum com implementação dentro da interface, marcado com a palavra‑chave default.

public interface Movable {
    void move(int x, int y);

    default void reset() {
        // Implementação típica: voltamos à origem do sistema de coordenadas
        move(0, 0);
    }
}

Explicação:

  • Todos os métodos de uma interface são, por padrão, public e abstract, mas métodos default não são abstratos: eles têm corpo.
  • A palavra‑chave default sempre vem antes do tipo de retorno do método.

Como isso fica em uma classe?

public class Robot implements Movable {
    private int x, y;

    @Override
    public void move(int x, int y) {
        this.x = x;
        this.y = y;
        System.out.println("Robô movido para (" + x + ", " + y + ")");
    }

    // não é obrigatório implementar reset() — a versão default funcionará!
}

Agora, se chamarmos reset() em um objeto Robot, a implementação da interface Movable será executada:

public class Main {
    public static void main(String[] args) {
        Movable robot = new Robot();
        robot.move(10, 20); // Robô movido para (10, 20)
        robot.reset();      // Robô movido para (0, 0)
    }
}

3. Métodos default na biblioteca padrão

Os métodos default foram adicionados justamente para permitir evoluir os enormes interfaces padrão do Java sem quebrar código legado.

Exemplo: interface List (Java 8+)

No Java 8, foram adicionados métodos com implementação na interface List, por exemplo, forEach, replaceAll, sort:

default void forEach(Consumer<Entity> action) {
    for (Entity e : this) {
        action.accept(e);
    }
}

Se você implementar sua própria lista e não sobrescrever forEach, ele ainda funcionará — graças ao método default.

Mais detalhes sobre tipos genéricos (Consumer<Entity>) você verá no nível 26 :P

4. Por que precisamos de métodos default?

  • Evoluir o API sem quebrar código: é possível adicionar novos métodos à interface sem a necessidade de implementá-los em todas as classes existentes.
  • Padrões de comportamento reutilizáveis: é possível declarar um comportamento padrão para que as classes possam usá-lo ou sobrescrevê-lo.
  • Redução de duplicação: se o comportamento é igual para a maioria das implementações, não é preciso copiar código em cada classe.

Analogia

Imagine que você tem um contrato de aluguel de apartamento (interface). Antes estava escrito: “O inquilino é obrigado a pagar pela água”. Depois adicionaram: “O inquilino é obrigado a pagar pela eletricidade”. Se não existissem métodos default, você teria que reescrever todos os contratos com todos os inquilinos! Com os métodos default — basta adicionar a cláusula e, se alguém precisar, pode negociar de outra forma.

5. Restrições e particularidades dos métodos default

Métodos default não podem sobrescrever métodos da classe Object

Você não pode declarar em uma interface um método default com a mesma assinatura de equals, hashCode ou toString da classe Object. Isso evita confusão: afinal, todo objeto em Java já possui esses métodos.

// Erro de compilação!
interface Broken {
    default boolean equals(Object obj) { return false; }
}

Conflitos de métodos default

E se uma classe implementar duas interfaces que possuem um método default com a mesma assinatura? O compilador Java dirá honestamente: “Resolva você mesmo, eu não sei o que fazer!”

interface A {
    default void hello() { System.out.println("Hello from A"); }
}

interface B {
    default void hello() { System.out.println("Hello from B"); }
}

class C implements A, B {
    // É obrigatório resolver o conflito:
    @Override
    public void hello() {
        // Você pode escolher qual método chamar ou implementar o seu
        A.super.hello(); // ou B.super.hello();
    }
}

Se não implementar hello() na classe C, haverá erro de compilação.

Métodos default podem chamar outros métodos da interface

Um método default pode chamar outros métodos da interface, até mesmo os abstratos. O importante é que exista implementação na classe.

interface Printer {
    void print(String text);

    default void printTwice(String text) {
        print(text);
        print(text);
    }
}

6. Exemplo: evoluindo um aplicativo com um método default

Vamos ver um exemplo de uso de métodos default na interface Movable:

public interface Movable {
    void move(int x, int y);

    default void reset() {
        move(0, 0);
    }
}

E há uma classe Robot que implementa essa interface:

public class Robot implements Movable {
    private int x = 5;
    private int y = 7;

    @Override
    public void move(int x, int y) {
        this.x = x;
        this.y = y;
        System.out.println("Robô movido para (" + x + ", " + y + ")");
    }

    // não implementamos reset() — usamos o método default!
}

Agora vamos chamar ambos os métodos:

public class Main {
    public static void main(String[] args) {
        Movable robot = new Robot();
        robot.move(10, 20); // Robô movido para (10, 20)
        robot.reset();      // Robô movido para (0, 0)
    }
}

Se quisermos que o Robot faça o reset de forma especial, basta sobrescrever reset() na classe:

@Override
public void reset() {
    System.out.println("O robô desliga e retorna à base!");
    move(0, 0);
}

7. Métodos default e implementação múltipla de interfaces

Os métodos default são especialmente úteis quando uma classe implementa várias interfaces. Mas há um detalhe: se ambas as interfaces tiverem um método default com a mesma assinatura, o compilador exigirá a resolução explícita do conflito.

Exemplo de conflito

interface A {
    default void show() { System.out.println("A"); }
}
interface B {
    default void show() { System.out.println("B"); }
}
class C implements A, B {
    @Override
    public void show() {
        // Escolhemos explicitamente qual método default usar
        A.super.show(); // ou B.super.show();
    }
}

8. Esquema: como funciona a chamada de um método default


+-------------------+
|   Movable         |
|-------------------|
| +move(int, int)   | <- método abstrato
| +reset()          | <- método default
+-------------------+
         ^
         |
+-------------------+
|   Robot           |
|-------------------|
| +move(int, int)   | <- implementa
|                   | (não implementa reset())
+-------------------+
         |
     Chamada de reset()
         |
   É usada a implementação
   da interface Movable
Chamada de método default: implementação padrão da interface

9. Erros comuns ao trabalhar com métodos default

Erro nº 1: tentar criar método default sem implementação.
O método default precisa ter corpo! Se você escrever default void foo();, o compilador dirá imediatamente: “Esqueceu as chaves?”

Erro nº 2: conflito de métodos default de interfaces diferentes.
Se a classe implementar duas interfaces com o mesmo método default, você deve resolver o conflito explicitamente — caso contrário, o compilador não permitirá compilar o código.

Erro nº 3: tentar declarar método default com a assinatura de um método de Object.
Não é possível criar método default equals, hashCode ou toString em uma interface — apenas métodos abstratos com esses nomes.

Erro nº 4: esquecer que métodos default não são “mágica”, apenas uma ferramenta prática.
Métodos default não anulam o princípio de que uma interface é um contrato. Se o comportamento padrão não servir, sempre sobrescreva o método default na classe.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION