Tradução de artigo escrito por Peter Verhas datado de abril de 2014. Do 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.
- altere a interface com o método abstrato m() e adicione uma implementação padrão.
- Compile a interface modificada.
- Execute a classe: erro.
Código de exemplo
Para 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étodomain
para 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$
GO TO FULL VERSION