Qual é o método padrão?
Após o lançamento do Java 8, novos métodos podem ser adicionados à interface, mas a interface ainda permanecerá compatível com sua classe de implementação. Isso é importante porque a biblioteca que você desenvolve pode ser amplamente utilizada por vários desenvolvedores. Antes do Java 8, depois que uma interface era publicada em uma biblioteca de classes, se um novo método fosse adicionado à interface, os aplicativos que implementavam essa interface corriam o risco de travar ao usar a nova versão da interface.
Com o Java 8, não existe esse perigo? A resposta é não.
Adicionar métodos padrão a uma interface pode tornar algumas classes de implementação indisponíveis.
Primeiro, vejamos os detalhes do método padrão.
No Java 8, métodos em interfaces podem ser implementados (métodos estáticos em Java 8 também podem ser implementados em interfaces, mas isso é outro tópico). O método implementado na interface é chamado de método padrão, que é identificado com a palavra-chave default como modificador. Quando uma classe implementa uma interface, ela pode implementar métodos que já estão implementados na interface, mas isso não é obrigatório. Esta classe herdará o método padrão. É por isso que quando a interface muda, a classe de implementação não precisa ser alterada.
E quanto a múltiplas heranças?
Quando uma classe implementa mais de uma (como duas) interfaces, e essas interfaces têm o mesmo método padrão, as coisas ficam muito complicadas. Qual método padrão a classe herda? Nenhum! Neste caso, a própria classe (seja diretamente ou uma classe superior na árvore de herança) deve implementar o método padrão.
O mesmo acontece quando uma interface implementa o método padrão e outra interface declara o método padrão como abstrato. Java 8 tenta evitar ambiguidade e manter o rigor. Se um método for declarado em múltiplas interfaces, nenhuma das implementações padrão será herdada e você receberá um erro em tempo de compilação.
No entanto, se você compilou sua classe, não haverá erros em tempo de compilação. Neste ponto, Java 8 é inconsistente. Tem suas próprias razões, e há várias razões que não quero explicar em detalhes ou discutir em profundidade aqui (porque: a versão foi lançada, o tempo de discussão é muito longo e esta plataforma nunca teve). tal discussão).
1. Suponha que você tenha duas interfaces e uma classe de implementação.
2. Uma das interfaces implementa um método padrão m().
3. Compile a interface e a classe de implementação juntas.
4. Modifique a interface que não contém o método m() e declare o método m() como abstrato.
5. Recompile a interface modificada separadamente.
6. Execute a classe de implementação.
1. Modifique a interface que contém o método abstrato m() e crie uma implementação padrão.
2. Compile a interface modificada
3. Executar classe: Falha.
Quando duas interfaces fornecem uma implementação padrão para o mesmo método, esse método não pode ser chamado, a menos que a classe de implementação também implemente o método padrão (diretamente ou por uma classe de nível superior na árvore de herança).
Código de exemplo:
Para demonstrar o exemplo acima, criei um diretório de teste para C.java e há 3 subdiretórios nele para armazenar I1.java e I2.java. O diretório de teste contém o código fonte C.java da classe C. O diretório base contém a versão da interface que pode ser compilada e executada. I1 contém o método m() com implementação padrão e I2 não contém nenhum método.
A classe de implementação contém o método principal para que possamos executá-lo em nossos testes. Ele verificará se existem parâmetros de linha de comando, para que possamos realizar facilmente testes chamando m() e não chamando m().
Copie o código do código da seguinte forma:
~/github/test$ gato C.java
classe pública C implementa I1, I2 {
public static void main(String[] args) {
C c = novo C();
if(args.comprimento == 0){
cm();
}
}
}
~/github/test$ catbase/I1.java
interface pública I1 {
padrão vazio m(){
System.out.println("olá interface 1");
}
}
~/github/test$ cat base/I2.java
interface pública I2 {
}
Use a seguinte linha de comando para compilar e executar:
Copie o código da seguinte forma:~/github/test$ javac -cp .:base C.java
~/github/teste$ java -cp.:base C
Olá interface 1
O diretório compatível contém a interface I2 com o método abstrato m() e a interface I1 não modificada.
Copie o código da seguinte forma: ~/github/test$ cat compatível/I2.java
interface pública I2 {
vazio m();
}
Isso não pode ser usado para compilar a classe C:
Copie o código da seguinte forma: ~/github/test$ javac -cp .:compatível C.java
C.java:1: erro: C não é abstrato e não substitui o método abstrato m() em I2
classe pública C implementa I1, I2 {
^
1 erro
A mensagem de erro é muito precisa. Como temos o C.class obtido na compilação anterior, se compilarmos as interfaces no diretório compatível, ainda obteremos duas interfaces que podem executar a classe de implementação:
Copie o código do código da seguinte forma:
~/github/test$ javac compatível/I*.java
~/github/test$ java -cp .:compatível C
Olá interface 1
O terceiro diretório chamado errado contém a interface I2 que também define o método m():
Copie o código do código da seguinte forma:
~/github/test$ gato errado/I2.java
interface pública I2 {
padrão vazio m(){
System.out.println("olá interface 2");
}
}
Deveríamos nos dar ao trabalho de compilá-lo. Embora o método m() seja definido duas vezes, a classe de implementação ainda pode ser executada, desde que não chame o método definido várias vezes. No entanto, desde que chamemos o método m(), ele falhará imediatamente. Aqui estão os parâmetros de linha de comando que usamos:
Copie o código do código da seguinte forma:
~/github/test$ javac errado/*.java
~/github/test$ java -cp .:C errado
Exceção no thread "principal" java.lang.IncompatívelClassChangeError: Conflitante
métodos padrão: I1.m I2.m
em Cm(C.java)
em C.main(C.java:5)
~/github/test$ java -cp .:C x errado
~/github/teste$
para concluir
Ao portar uma biblioteca de classes que adiciona uma implementação padrão a uma interface para o ambiente Java 8, geralmente não haverá problema. Pelo menos foi isso que os desenvolvedores da biblioteca de classes Java8 pensaram quando adicionaram métodos padrão às classes de coleção. Os aplicativos que usam sua biblioteca ainda dependem de bibliotecas Java 7 que não possuem métodos padrão. Ao usar e modificar diversas bibliotecas de classes diferentes, há uma pequena chance de ocorrerem conflitos. Como isso pode ser evitado?
Projete sua biblioteca de classes como antes. Não leve isso a sério ao confiar no método padrão. Não use como último recurso. Escolha os nomes dos métodos com sabedoria para evitar conflitos com outras interfaces. Aprenderemos como usar esse recurso para desenvolvimento em programação Java.