tutorial expresiones java lambda nullpointerexception java-8 method-reference

java - expresiones - lambda c# tutorial



java.lang.NullPointerException se genera utilizando una referencia de método pero no una expresión lambda (2)

Este comportamiento se basa en una sutil diferencia entre el proceso de evaluación de referencias de método y expresiones lambda.

De la evaluación de tiempo de ejecución JLS de las referencias de métodos :

Primero, si la expresión de referencia del método comienza con ExpressionName o Primary, se evalúa esta subexpresión. Si la subexpresión se evalúa como null , se NullPointerException una NullPointerException y la expresión de referencia del método se completa abruptamente .

Con el siguiente código:

Thread t = new Thread(s::toLowerCase); // <-- s is null, NullPointerException thrown here t.setUncaughtExceptionHandler((t1, e) -> System.out.println("Exception!"));

la expresión s se evalúa como null y se null una excepción exactamente cuando se evalúa esa referencia de método. Sin embargo, en ese momento, no se adjuntó ningún controlador de excepción, ya que este código se ejecutaría después.

Esto no sucede en el caso de una expresión lambda, porque la lambda se evaluará sin que se ejecute su cuerpo. De la evaluación en tiempo de ejecución de las expresiones Lambda :

La evaluación de una expresión lambda es distinta de la ejecución del cuerpo lambda.

Thread t = new Thread(() -> s.toLowerCase()); t.setUncaughtExceptionHandler((t1, e) -> System.out.println("Exception!"));

Incluso si s es null , la expresión lambda se creará correctamente. Luego se adjuntará el controlador de excepciones, se iniciará el subproceso, lanzando una excepción, que será capturada por el controlador.

Como nota al margen, parece que Eclipse Mars.2 tiene un pequeño error al respecto: incluso con la referencia del método, invoca el controlador de excepciones. Eclipse no está lanzando una NullPointerException en s::toLowerCase cuando debería, aplazando así la excepción más adelante, cuando se agregó el controlador de excepciones.

He notado algo extraño sobre las excepciones no controladas que utilizan la referencia de método Java 8. Este es mi código, usando la expresión lambda () -> s.toLowerCase() :

public class Test { public static void main(String[] args) { testNPE(null); } private static void testNPE(String s) { Thread t = new Thread(() -> s.toLowerCase()); // Thread t = new Thread(s::toLowerCase); t.setUncaughtExceptionHandler((t1, e) -> System.out.println("Exception!")); t.start(); } }

Imprime "Excepción", por lo que funciona bien. Pero cuando cambio Thread t para usar una referencia de método (incluso IntelliJ sugiere eso):

Thread t = new Thread(s::toLowerCase);

la excepción no está siendo atrapada:

Exception in thread "main" java.lang.NullPointerException at Test.testNPE(Test.java:9) at Test.main(Test.java:4) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

¿Alguien puede explicar lo que está pasando aquí?


Guau. Has descubierto algo interesante. Echemos un vistazo a lo siguiente:

Function<String, String> stringStringFunction = String::toLowerCase;

Esto nos devuelve una función que acepta un parámetro de tipo String y devuelve otra String , que es una minúscula del parámetro de entrada. Esto es algo equivalente al s.toLowerCase() , donde s es el parámetro de entrada.

stringStringFunction(param) === param.toLowerCase()

próximo

Function<Locale, String> localeStringFunction = s::toLowerCase;

es una función de Locale a String . Esto es equivalente a la invocación del método s.toLowerCase(Locale) . Funciona, bajo el capó, en 2 parámetros: uno es s otro es algo local. Si s es null , entonces esta creación de función NullPointerException una NullPointerException .

localeStringFunction(locale) === s.toLowerCase(locale)

El siguiente es

Runnable r = () -> s.toLowerCase()

Que es una implementación de la interfaz Runnable que, cuando se ejecuta, llamará al método toLowerCase en una cadena dada.

Entonces en tu caso

Thread t = new Thread(s::toLowerCase);

intenta crear un nuevo Thread pase el resultado de la invocación de s::toLowerCase . Pero esto arroja un NPE a la vez. Incluso antes de que se inicie el hilo. Y así, NPE se lanza en su hilo actual, no desde el hilo interno t . Es por eso que su controlador de excepciones no se ejecuta.