W3docs

Interfaces em Java

Defina contratos em Java com interfaces — métodos abstratos, métodos padrão e herança múltipla de tipo.

Uma interface é um contrato: um conjunto nomeado de operações que qualquer classe que a implemente se compromete a fornecer. Interfaces não possuem estado de instância, construtores, e (com uma exceção abordada no próximo capítulo) nenhum corpo de método. Elas descrevem o que um tipo pode fazer, deixando cada como para as implementações.

Interfaces são a resposta do Java à herança múltipla. Uma classe estende exatamente uma classe, mas pode implementar qualquer número de interfaces — permitindo que você combine Comparable, AutoCloseable e Iterable no mesmo tipo sem ambiguidade.

Declarando uma interface

Use interface em vez de class:

public interface Shape {
  double area();              // implicitly public abstract
  double perimeter();
}

As declarações de método em uma interface são implicitamente public abstract. Você pode escrever esses modificadores, mas a maioria dos guias de estilo os omite por serem redundantes.

Implementando uma interface

Uma classe declara isso com implements. A classe deve fornecer um corpo para cada método que a interface declara:

public class Circle implements Shape {
  private final double r;
  public Circle(double r) { this.r = r; }

  @Override public double area()      { return Math.PI * r * r; }
  @Override public double perimeter() { return 2 * Math.PI * r; }
}

Se uma classe implementa uma interface mas não fornece todos os métodos, ela deve ser declarada abstract — a mesma regra das classes abstratas.

Você também pode implementar várias interfaces de uma vez:

public class Money implements Comparable<Money>, java.io.Serializable {
  private final long cents;
  public Money(long cents) { this.cents = cents; }
  public int compareTo(Money other) { return Long.compare(this.cents, other.cents); }
}

Isso é "herança múltipla de tipo" — Money é ao mesmo tempo um Comparable<Money> e um Serializable. Código que precise de qualquer um deles pode aceitar um Money.

Programando para uma interface

O propósito das interfaces é escrever código contra o contrato, não a implementação:

public double sumAreas(List<Shape> shapes) {
  double sum = 0;
  for (Shape s : shapes) sum += s.area();
  return sum;
}

sumAreas não sabe nem se importa com Circle. Adicione Square implements Shape, Triangle implements Shape, e a função funciona em listas deles também — sem alterações.

A biblioteca padrão é construída sobre esse padrão. Você quase sempre declara uma variável de um tipo de interface e instancia uma concreta:

List<String>   names = new ArrayList<>();        // List, not ArrayList
Map<String, Integer> counts = new HashMap<>();   // Map,  not HashMap

Se você mais tarde mudar para LinkedList ou LinkedHashMap, apenas a linha do new precisa ser alterada.

Constantes em interfaces

Todo campo declarado em uma interface é implicitamente public static final — uma constante. Normalmente não se adicionam constantes a interfaces (é considerado má prática — o Constant Interface Antipattern), mas a sintaxe existe:

public interface Color {
  String DEFAULT = "black";    // implicitly public static final
}

Se você precisar de constantes, prefira um enum ou uma final class simples com campos public static final.

Interfaces podem estender interfaces

Uma interface pode estender outras interfaces — inclusive várias ao mesmo tempo:

public interface Readable    { String read(); }
public interface Writable    { void write(String s); }
public interface ReadWrite extends Readable, Writable { }

Agora qualquer classe que implementa ReadWrite precisa fornecer tanto read() quanto write(). Não há distinção entre class extends / interface implements aqui — interfaces simplesmente estendem interfaces com extends.

Métodos padrão e estáticos (prévia)

Desde o Java 8, interfaces podem ter métodos default (corpos fornecidos pela palavra-chave default) e métodos static. Eles permitem adicionar comportamento a uma interface sem quebrar cada implementador existente. O tratamento completo está no próximo capítulo, métodos padrão:

public interface Shape {
  double area();

  // Default method — implementors get this for free.
  default String describe() {
    return getClass().getSimpleName() + " area=" + area();
  }

  // Static factory on the interface itself.
  static Shape unitCircle() { return new Circle(1); }
}

Interfaces marcadoras

Uma interface marcadora não declara nenhum método. Ela existe puramente como um rótulo que o código em tempo de execução pode verificar:

public interface Cacheable { }      // no methods

public class Snapshot implements Cacheable { ... }

E em algum lugar: if (obj instanceof Cacheable) { ... }. O Java moderno prefere anotações para esse tipo de metadado (@Cacheable em vez de implements Cacheable), mas Serializable, Cloneable e RandomAccess são interfaces marcadoras bem conhecidas na biblioteca padrão.

Interfaces funcionais

Uma interface com exatamente um método abstrato é uma interface funcional. Isso importa porque o Java permite que você forneça tal interface com uma expressão lambda ou referência de método em vez de uma classe anônima completa — o lambda é a implementação desse único método.

@FunctionalInterface
public interface Transformer {
  String apply(String input);     // the single abstract method
}

Transformer upper = s -> s.toUpperCase();   // lambda implements apply
System.out.println(upper.apply("hi"));      // prints: HI

A anotação opcional @FunctionalInterface é uma proteção em tempo de compilação: o código não compilará se a interface acabar com mais de um método abstrato. (Métodos default e static não contam para o limite.) A biblioteca padrão traz uma caixa de ferramentas completa deles — Runnable, Comparator, Function, Predicate, Supplier — abordados em interfaces funcionais.

Escolhendo entre uma interface e uma classe abstrata

A árvore de decisão:

  1. As subclasses precisam compartilhar estado ou implementações de métodos compartilhados? Se sim, você provavelmente quer uma classe abstrata — interfaces não podem manter estado de instância.
  2. Você quer um tipo que várias classes não relacionadas possam satisfazer? Se sim, uma interface — uma classe pode implementar muitas interfaces, mas estender apenas uma classe.
  3. O contrato vai crescer com o tempo? Interfaces evoluem com mais cuidado — adicionar um método abstrato a uma interface quebra todos os implementadores, a menos que você o torne um método default. Classes abstratas podem adicionar um método concreto sem quebrar ninguém.

Na maioria dos códigos reais, interfaces vencem para contratos de tipo, e classes abstratas aparecem nos bastidores quando várias implementações compartilham infraestrutura.

Um exemplo completo

java— editable, runs on the server

O que vem a seguir

As interfaces costumavam ser contratos puros — sem corpos de método algum. Desde o Java 8 isso foi relaxado: interfaces podem fornecer métodos default, métodos static e até métodos auxiliares private. O próximo capítulo é um tour por essas adições. Continue em métodos padrão.

Prática

Prática
Qual é a razão prática pela qual o Java não permite que uma classe estenda mais de uma classe, mas permite que uma classe implemente muitas interfaces?
Qual é a razão prática pela qual o Java não permite que uma classe estenda mais de uma classe, mas permite que uma classe implemente muitas interfaces?
Was this page helpful?