JavaRush /Blogue Java /Random-PT /Métodos padrão em Java 8: o que eles podem e o que não po...
Spitfire
Nível 33

Métodos padrão em Java 8: o que eles podem e o que não podem fazer?

Publicado no grupo Random-PT
Tradução de artigo escrito por Peter Verhas datado de abril de 2014. Métodos padrão em Java 8: o que eles podem e o que não podem fazer?  - 1Do tradutor: o termo " método padrão " acaba de aparecer em Java e não tenho certeza se existe uma tradução estabelecida para ele em russo. Usarei o termo "método padrão", embora não ache que seja o ideal. Convido você a discutir uma tradução mais bem-sucedida.

Qual é o método padrão

Agora, com o lançamento do Java 8, você pode adicionar novos métodos às interfaces para que a interface permaneça compatível com as classes que a implementam. Isso é muito importante se você estiver desenvolvendo uma biblioteca usada por muitos programadores de Kiev a Nova York. Antes do Java 8, se você definisse uma interface em uma biblioteca, não poderia adicionar métodos a ela sem correr o risco de que algum aplicativo executando sua interface quebrasse quando ela fosse atualizada. Então, no Java 8 você não pode mais ter medo disso? Não, você não pode. Adicionar um método padrão a uma interface pode inutilizar algumas classes. Vejamos primeiro as vantagens dos métodos padrão. No Java 8, o método pode ser implementado diretamente na interface. (Métodos estáticos em uma interface agora também podem ser implementados, mas isso é outra história.) Um método implementado em uma interface é chamado de método padrão e é denotado pela palavra-chave padrão . Se uma classe implementa uma interface, ela pode, mas não é obrigada a, implementar os métodos implementados na interface. A classe herda a implementação padrão. É por isso que não é necessário modificar as classes ao alterar a interface que elas implementam.

Herança múltipla?

As coisas ficam mais complicadas se uma classe implementa mais de uma (digamos, duas) interfaces e implementa o mesmo método padrão. Qual método a classe herdará? A resposta é nenhuma. Neste caso, a classe deve implementar o próprio método (diretamente ou herdando-o de outra classe). A situação é semelhante se apenas uma interface tiver um método padrão e na outra o mesmo método for abstrato. Java 8 tenta ser disciplinado e evitar situações ambíguas. Se os métodos forem declarados em mais de uma interface, nenhuma implementação padrão será herdada pela classe - você receberá um erro de compilação. Porém, você pode não receber um erro de compilação se sua classe já estiver compilada. Java 8 não é robusto o suficiente nesse aspecto. Há razões para isso, que não quero discutir (por exemplo: a versão Java já foi lançada e o tempo para discussões já passou e, em geral, este não é o lugar para elas).
  • Digamos que você tenha duas interfaces e uma classe implemente ambas.
  • Uma das interfaces implementa o método padrão m().
  • Você compila todas as interfaces e a classe.
  • Você altera uma interface que não possui um método m() declarando-a como um método abstrato.
  • Você compila apenas a interface modificada.
  • Comece a aula.
Métodos padrão em Java 8: o que eles podem e o que não podem fazer?  - 2Neste caso a classe funciona. Você não pode compilá-lo com as interfaces atualizadas, mas foi compilado com versões mais antigas e, portanto, funciona. Agora
  • altere a interface com o método abstrato m() e adicione uma implementação padrão.
  • Compile a interface modificada.
  • Execute a classe: erro.
Quando há duas interfaces que fornecem uma implementação padrão de um método, esse método não pode ser chamado em uma classe, a menos que seja implementado pela própria classe (novamente, por conta própria ou herdado de outra classe). Métodos padrão em Java 8: o que eles podem e o que não podem fazer?  - 3Classe compatível. Ele pode ser carregado com uma interface modificada. Ele pode até ser executado até que seja chamado um método que tenha uma implementação padrão em ambas as interfaces.

Código de exemplo

Métodos padrão em Java 8: o que eles podem e o que não podem fazer?  - 4Para demonstrar o acima, criei um diretório de teste para a classe C.java e 3 subdiretórios para as interfaces nos arquivos I1.java e I2.java. O diretório raiz do teste contém o código-fonte da classe C.java. O diretório base contém uma versão das interfaces que são adequadas para execução e compilação: a interface I1 possui um método padrão m(); A interface I2 ainda não possui métodos. A classe possui um método mainpara que possamos executá-lo para testá-lo. Ele verifica se há algum argumento de linha de comando, para que possamos executá-lo facilmente com ou sem chamar o arquivo m().
~/github/test$ cat C.java
public class C implements I1, I2 {
  public static void main(String[] args) {
    C c = new C();
    if( args.length == 0 ){
      c.m();
    }
  }
}
~/github/test$ cat base/I1.java
public interface I1 {
  default void m(){
    System.out.println("hello interface 1");
  }
}
~/github/test$ cat base/I2.java
public interface I2 {
}
Você pode compilar e executar a classe na linha de comando.
~/github/test$ javac -cp .:base C.java
~/github/test$ java -cp .:base C
hello interface 1
O diretório compatível contém uma versão da interface I2 que declara o método m() como abstrato e também, por motivos técnicos, uma cópia não modificada de I1.java.
~/github/test$ cat compatible/I2.java

public interface I2 {
  void m();
}
Tal conjunto não pode ser usado para compilar uma classe C:
~/github/test$ javac -cp .:compatible C.java
C.java:1: error: C is not abstract and does not override abstract method m() in I2
public class C implements I1, I2 {
       ^
1 error
A mensagem de erro é muito precisa. Porém, temos C.class de uma compilação anterior e, se compilarmos as interfaces no diretório compatível, teremos duas interfaces que ainda poderão ser utilizadas para executar a classe:
~/github/test$ javac compatible/I*.java
~/github/test$ java -cp .:compatible C
hello interface 1
O terceiro diretório - wrong- contém a versão I2, que também declara o método m():
~/github/test$ cat wrong/I2.java
public interface I2 {
  default void m(){
    System.out.println("hello interface 2");
  }
}
Você nem precisa se preocupar com a compilação. Mesmo que o método seja declarado duas vezes, a classe ainda pode ser usada e executada até que o método m() seja chamado. É para isso que precisamos do argumento da linha de comando:
~/github/test$ javac wrong/*.java
~/github/test$ java -cp .:wrong C
Exception in thread "main" java.lang.IncompatibleClassChangeError: Conflicting default methods: I1.m I2.m
    at C.m(C.java)
    at C.main(C.java:5)
~/github/test$ java -cp .:wrong C x
~/github/test$

Conclusão

Ao portar sua biblioteca para Java 8 e alterar suas interfaces para incluir métodos padrão, você provavelmente não terá problemas. Pelo menos é isso que os desenvolvedores da biblioteca Java 8 esperam ao adicionar funcionalidades. Os aplicativos que usam sua biblioteca ainda a usam no Java 7, onde não há métodos padrão. Se várias bibliotecas forem usadas juntas, existe a possibilidade de conflito. Como evitá-lo? Projete a API da sua biblioteca da mesma forma que antes. Não fique complacente confiando nos recursos dos métodos padrão. Eles são um último recurso. Escolha os nomes com cuidado para evitar colisões com outras interfaces. Vamos ver como será o desenvolvimento para Java usando esse recurso.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION