Por que existe um método padrão?
O Java 8 está chegando. Como mencionado anteriormente, discutimos muito sobre esse tema antes, mas a expressão de Lambdas não é a única mudança de regras de jogo no Java 8.
Suponha que o Java 8 tenha sido lançado e contenha Lambda. Agora você planeja usar o Lambda.
List <?> List = ... list.faceach (…);
A definição de foreach não pode ser encontrada em java.util.list ou java.util.collection. Geralmente é uma solução para adicionar novos métodos e implementação às interfaces relevantes no JDK. No entanto, para o lançamento da versão, é impossível adicionar um novo método à interface, sem afetar a implementação existente.
Portanto, se o Lambda for usado no Java 8, não será possível ser usado para a biblioteca de coleções devido à compatibilidade a seguir.
Pelas razões acima, um novo conceito foi introduzido. O método de extensão virtual, que geralmente é o método de defesa, agora pode ser adicionado à interface, para que a implementação padrão do comportamento da instrução possa ser fornecida.
Simplificando, a interface Java agora pode implementar o método. A vantagem do método padrão é que ele pode adicionar um novo método padrão à interface sem destruir a implementação da interface.
Na minha opinião, esse não é o tipo de característica de Java que será usada todos os dias, mas definitivamente pode usar o Lambda para usar o Lambda naturalmente.
O exemplo mais simples
Vamos ver o exemplo mais simples: uma interface A, a classe Clazz implementa a interface A.
Interface pública A {padrão void foo () {System.out.println ("Calling A.Foo ()");} classe pública Classe Classe A {}O código pode ser compilado, mesmo que a classe Clazz não implemente o método Foo (). A implementação padrão do método foo () é fornecida na interface A.
Código do cliente usando este exemplo:
Clazz clazz = novo clazz ();
Herança múltipla?
Há uma pergunta comum: as pessoas farão quando ouvirem os novos recursos do método padrão "" Se uma classe implementa duas interfaces, e ambas as interfaces definem o método padrão com a mesma assinatura. Com exemplos anteriores:
Interface pública a {padrão void foo () {System.out.println ("Calling A.Foo ()");Este código não pode compilar os seguintes motivos:
Java: Classe Clazz dos tipos A a B a Foo () herda o valor padrão não relacionado
Para reparar isso, em Clazz, temos que resolver manualmente o método de reescrever o conflito:
Public class Clazz implementa a, b {public void foo () {}}} Mas o que devemos fazer se quisermos chamar o método de implementação padrão da interface A, em vez de implementar nosso próprio método? Isso é possível, cite o foo () em a, como mostrado abaixo:
Public class Clazz implementa a, b {public void foo () {a.super.foo ();}}} Agora não posso estar muito convencido de que gosto desta solução final. Talvez seja mais conciso do que a realização do método padrão na assinatura, conforme declarado no rascunho de primeira mão da especificação do método padrão:
Public class Clazz implementa a, b {public void foo () padrão a.foo;}Mas isso muda a gramática, não é? Se a interface A e a interface B definirem muitos métodos padrão que conflitam entre si, e estou disposto a usar o método padrão de toda a interface A para resolver o conflito? No momento, tenho que resolver o conflito um após o outro para reescrever todos os pares de conflitos. Isso pode exigir muito trabalho e escrever um grande número de código de modelo.
Estimo que o método de resolver o conflito requer muitas discussões, mas parece que o criador decide aceitar o desastre inevitável.
Exemplo real
Os exemplos reais do método padrão podem ser encontrados no saco inicial do JDK8. De volta ao exemplo do método de coleta da coleção, podemos descobrir que na interface java.lang.iterable, sua implementação padrão é a seguinte:
@FunctionInterfacePublic Interface iterable <T> {ITERATOR <T> ITERATOR (); A foreach usa um parâmetro de java.util.function.consumer Tipo de interface da função, que nos permite passar uma expressão de lambda ou uma referência de método, como segue:
List <?> List = ... list.faceach (System.out :: println);
Chamada de método
Vamos dar uma olhada em como o método padrão é realmente chamado. Se você não estiver familiarizado com esse problema, poderá estar interessado em ler o Rebel Labs sobre o Java Bytes.
Do ponto de vista do código do cliente, o método padrão é apenas um método virtual comum. Portanto, o nome deve ser um método de extensão virtual. Portanto, para um exemplo simples do método padrão como interface, o código do cliente chamará automaticamente a interface no local onde o método padrão é chamado.
Um clazz = novo clazz ();
Se o conflito do método padrão tiver sido resolvido, quando modificamos o método padrão e especificamos uma das interfaces, o InvoKespial especificará a implementação da implementação da interface da chamada específica.
Classe pública implementa a, b {public void foo () {a.super.foo (); A seguir, é apresentada a saída do Javap:
public void Foo ();
Como você vê: as instruções do InvokesPepecial são usadas para chamar o método da interface foo (). Do ponto de vista do bytecode, isso ainda é uma coisa nova, porque antes que você só possa chamar o método apontando para uma classe (pai) em vez de um super esse ponto para uma interface.
afinal…
O método padrão é o suplemento interessante ao idioma Java. O principal objetivo da expressão padrão é evoluir a interface JDK padrão e, quando finalmente começamos a usar a expressão Lambdas do Java 8, nos fornecemos uma experiência de transição suave. Quem sabe, talvez vejamos mais métodos padrão no design da API no futuro.