W3docs

Métodos Default e Static em Interfaces Java

Adicione métodos default e static a interfaces Java para evoluir APIs sem quebrar implementações existentes.

Até o Java 7, uma interface só podia declarar métodos abstratos — os corpos ficavam nas classes implementadoras. O Java 8 adicionou três novidades que você pode colocar dentro de uma interface:

  • Métodos default — um método com corpo, herdado pelos implementadores que não o sobrescrevem.
  • Métodos static — métodos utilitários pertencentes à própria interface.
  • Métodos private (Java 9) — auxiliares compartilhados entre os métodos default e static da interface.

O caso de uso motivador foi a evolução de APIs. Depois que java.util.Collection existia há quinze anos com milhões de implementações, a equipe Java queria adicionar stream() sem quebrar todas elas. default Stream<E> stream() { ... } fez exatamente isso.

Métodos default

Um método default possui um corpo. As classes implementadoras o herdam gratuitamente e podem sobrescrevê-lo caso queiram um comportamento diferente:

public interface Greeter {
  String name();

  default String greet() {
    return "Hello, " + name() + "!";
  }
}

public class English implements Greeter {
  public String name() { return "Alice"; }
}

public class Loud implements Greeter {
  public String name()  { return "Bob"; }
  public String greet() { return "HEY " + name().toUpperCase() + "!"; }   // override
}

new English().greet();   // Hello, Alice!
new Loud().greet();      // HEY BOB!

default é apenas um marcador — a palavra-chave informa ao compilador "este é um corpo de método dentro de uma interface." Sem ela, um método de interface não tem corpo.

Como todo membro de interface não private, um método default é implicitamente public; você não pode torná-lo protected ou de acesso de pacote. Não há a palavra-chave public nos exemplos acima porque essa é a única opção que o compilador permite.

Adicionar um método default a uma interface existente é uma mudança não disruptiva. Os implementadores que não o sobrescrevem herdam o padrão. Essa é a propriedade que torna possível a evolução de interfaces.

Métodos static em interfaces

Um método static em uma interface pertence à interface, não às instâncias. Chame-o pelo nome da interface:

public interface Path {
  static Path of(String s) { return new SimplePath(s); }
  String value();
}

Path p = Path.of("/tmp/foo");

Um uso comum são os métodos de fábrica que produzem instâncias da interface — Path.of, List.of, Map.of, Stream.of. Eles permitem que os chamadores dependam da interface mesmo durante a construção, em vez de ter que escolher uma classe de implementação específica.

Métodos estáticos de interface não são herdados. Você sempre os chama pelo nome da interface, nunca por uma subclasse.

Métodos private (Java 9+)

Um método private em uma interface é um auxiliar visível apenas para outros métodos da mesma interface. Ele permite que dois métodos default compartilhem lógica sem expô-la aos implementadores:

public interface Logger {
  default void info(String msg) { log("INFO", msg); }
  default void warn(String msg) { log("WARN", msg); }

  private void log(String level, String msg) {
    System.out.println("[" + level + "] " + msg);
  }
}

Sem private, você teria que copiar o corpo nos dois métodos default ou expor log como um método default — o que permitiria que os implementadores o sobrescrevessem, o que provavelmente não é desejado.

O que métodos default não podem fazer

Os métodos default vivem na interface, que não possui estado. Eles podem chamar métodos abstratos e outros métodos default/static, mas não podem ler ou escrever campos de instância — não há nenhum para ler.

public interface Counter {
  int get();
  void increment();

  default void incrementTwice() {     // ok — only calls abstract methods
    increment();
    increment();
  }
}

Isso limita o quanto de comportamento real um método default pode carregar. Eles são mais adequados para camadas finas de conveniência sobre os métodos abstratos, não para substituir a implementação por completo.

Conflitos em diamante

Quando uma classe implementa duas interfaces que fornecem um método default com a mesma assinatura, a classe deve resolver o conflito explicitamente:

interface A { default String hello() { return "A"; } }
interface B { default String hello() { return "B"; } }

class C implements A, B {
  @Override
  public String hello() {
    return A.super.hello() + B.super.hello();   // explicit pick
  }
}

A.super.hello() invoca o default de A; B.super.hello() invoca o de B. O compilador se recusa a compilar class C implements A, B { } sem uma sobrescrita — não há um vencedor automático. Esta é a resposta do Java ao "problema do diamante" de herança múltipla — quando ele aparece, a classe precisa fazer a escolha.

Uma subclasse também tem precedência sobre um default herdado: se uma superclasse concreta fornece hello(), isso prevalece sobre qualquer método default de uma interface.

Quando adicionar um método default

default é a ferramenta certa quando:

  • Você quer estender uma interface existente e amplamente implementada sem quebrar os implementadores.
  • O novo método é genuinamente derivável dos métodos abstratos existentes.
  • O default é "obviamente correto" — a maioria dos implementadores ficará satisfeita com ele.

É a ferramenta errada quando:

  • Você está tentado a colocar comportamento real baseado em estado compartilhado (use uma classe abstrata em vez disso).
  • O default não é realmente útil e os implementadores vão sobrescrevê-lo de qualquer forma (deixe-o abstrato).

Um exemplo trabalhado

java— editable, runs on the server

O que vem a seguir

Interfaces e classes abstratas tratam de como os tipos se relacionam entre arquivos. O próximo subtópico — classes aninhadas — trata de como os tipos se relacionam dentro de um único arquivo: classes declaradas dentro de outras classes e os quatro tipos que o Java oferece para elas. Continue para classes aninhadas.

Prática

Prática
Qual é o principal benefício prático de marcar um novo método de interface como default em vez de deixá-lo abstrato?
Qual é o principal benefício prático de marcar um novo método de interface como default em vez de deixá-lo abstrato?
Was this page helpful?