Avaliação de referências de métodos em Java
1. Visão geral
O Java 8 introduziu o conceito de referências de método. Costumamos vê-los como semelhantes às expressões lambda.
No entanto, referências de método e expressões lambda não são exatamente a mesma coisa. Neste artigo, mostraremos por que eles são diferentes e quais são os riscos de usá-los da maneira errada.
2. Sintaxe de referências de método e lambdas
Para começar, vamos ver alguns exemplos de expressões lambda:
Runnable r1 = () -> "some string".toUpperCase();
Consumer c1 = x -> x.toUpperCase();
E alguns exemplos de referências de método:
Function f1 = String::toUpperCase;
Runnable r2 = "some string"::toUpperCase;
Runnable r3 = String::new;
Esses exemplos podem nos fazer pensar nas referências de métodos como uma notação abreviada para lambdas.
Mas vamos dar uma olhada noOracle documentation oficial. Podemos encontrar um exemplo interessante lá:
(test ? list.replaceAll(String::trim) : list) :: iterator
Como podemos ver, a Java Language Specification nos permite ter um tipo diferente de expressão antes do operador de dois pontos. A parte antes de:: é chamadathetarget reference.
A seguir, discutiremos o processo de avaliação de referência do método.
3. Avaliação de referência de método
O que acontecerá quando executarmos o código a seguir?
public static void main(String[] args) {
Runnable runnable = (f("some") + f("string"))::toUpperCase;
}
private static String f(String string) {
System.out.println(string);
return string;
}
Acabamos de criar um objetoRunnable. Nada mais nada menos. No entanto, a saída é:
some
string
Aconteceu porque otarget reference is evaluated when the declaration is first spotted.. Portanto, perdemos a preguiça desejada. The target reference is also evaluated only once. Então, se adicionarmos esta linha ao exemplo acima:
runnable.run()
Não veremos nenhuma saída. E o próximo caso?
SomeWorker worker = null;
Runnable workLambda = () -> worker.work() // ok
Runnable workMethodReference = worker::work; // boom! NullPointerException
A explicação fornecida porthe documentation mencionado antes:
“Uma expressão de invocação de método (§15.12) que invoca um método de instância lança uma NullPointerException se a referência de destino for nula.”
A melhor maneira de evitar situações inesperadas pode sernever use variable access and complex expressions as target references.
Uma boa idéia pode ser usar referências de método apenas como uma notação curta e clara para seu equivalente lambda. Ter apenas um nome de classe antes do operador:: garante a segurança.
4. Conclusão
Neste artigo, aprendemos sobre o processo de avaliação de referências de método.
Conhecemos os riscos e as regras que devemos seguir para não ser surpreendidos repentinamente pelo comportamento de nosso aplicativo.