1. Métodos de função

Se uma interface tiver apenas um método , uma variável desse tipo de interface pode receber um valor fornecido por uma expressão lambda (função lambda). Essas interfaces ficaram conhecidas como interfaces funcionais (depois que Java adicionou suporte para funções lambda).

Por exemplo, Java tem a Consumer<Type>interface, que tem o accept(Type obj)método. Por que essa interface é necessária?

No Java 8, as coleções têm um forEach()método que permite executar alguma ação para cada elemento da coleção . E aqui a Consumer<T>interface funcional é usada para passar a ação para o forEach()método.

Veja como você pode exibir todos os elementos de uma coleção :

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");

list.forEach( (s) -> System.out.println(s) );
Exibindo todos os elementos de uma coleção (usando uma expressão lambda)

O compilador irá converter o código acima para o código abaixo:

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");

list.forEach(new Consumer<String>()
{
   public void accept(String s)
   {
      System.out.println(s));
   }
});
Exibindo todos os elementos de uma coleção (usando uma classe anônima)

A primeira versão é definitivamente mais curta que a segunda. E embora o código com expressões lambda seja difícil de ler, o código com classes internas anônimas às vezes é ainda mais difícil de ler.



2. Referência do método

No entanto, nosso código de expressão lambda pode ser escrito ainda mais curto.

Primeiro, você pode omitir os parênteses ao redor do sparâmetro:

list.forEach( (s) -> System.out.println(s) );
Antes
list.forEach( s -> System.out.println(s) );
Depois

Isso só pode ser feito se houver um parâmetro . Se houver vários parâmetros, você deverá usar parênteses .

E segundo, você pode escrever assim:

list.forEach( System.out::println );
Notação mais compacta

Esta é exatamente a mesma notação. Observe que não há parênteses após o println.

Aqui temos o mesmo código — uma chamada de método:

object::method
x -> object.method(x)

Pense nisso: queríamos realizar alguma ação para cada elemento da listcoleção. Se a ação for uma única chamada de função (como println()), faz sentido simplesmente passar a função para o método como um parâmetro.

Mas como explicamos ao compilador que queremos passar o método em vez de chamá-lo? Para fazer isso, em vez do operador ponto, usamos dois dois pontos antes do nome do método. Um único ponto já é usado para indicar o operador ternário.

Esta é a notação mais simples e compacta.



3. Construtor

As referências de método com dois-pontos duplos são muito úteis quando trabalhamos com fluxos de E/S. Você verá isso um pouco mais tarde.

Enquanto isso, vamos falar sobre 3 maneiras populares de passar uma referência de método:

Referência a um método de um objeto

Para passar uma referência a um método de um objeto, você precisa escrever algo como . Este código é equivalente a .object::method
x -> object.method(x)

As variáveis ​​especiais thise superpodem ser usadas como o objeto.

Referência a um método de uma classe

Para passar uma referência a um método estático, você precisa escrever algo como . Este código é convertido em código comoclass::methodx -> class.method(x);

Referência a um construtor

Um construtor se comporta de maneira semelhante a um método de classe estático, portanto, você também pode passar uma referência a um construtor. Fica assim: .class::new

Por exemplo, você pode contornar o apagamento de tipos para coleções e passar ao toArray()método uma referência a um construtor que criará o array desejado:toArray(int[]::new);