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) );
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));
}
});
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 s
parâmetro:
list.forEach( (s) -> System.out.println(s) );
list.forEach( s -> System.out.println(s) );
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 );
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 list
coleçã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 this
e super
podem 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::method
x -> 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);
GO TO FULL VERSION