java.lang.NullPointerException jest rzucany przy użyciu referencji metody, ale nie wyrażenia lambda
Zauważyłem coś dziwnego w nieobsługiwanych wyjątkach przy użyciu Java 8 method reference. To jest mój kod, używając wyrażenia 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();
}
}
Wyświetla "wyjątek", więc działa dobrze. Ale kiedy zmienię Thread t
, Aby użyć metody-referencji (nawet IntelliJ sugeruje, że):
Thread t = new Thread(s::toLowerCase);
Wyjątek nie jest złapany:
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)
Czy ktoś może wyjaśnić, co tu się dzieje? 2 answers
To zachowanie opiera się na subtelnej różnicy między procesem oceny referencji metody a wyrażeniami lambda.
From the JLS Run-Time Evaluation of Method References :
Po pierwsze, jeśli wyrażenie odniesienia do metody zaczyna się od nazwy wyrażenia lub wyrażenia podstawowego, to podwyrażenie jest obliczane. Jeśli podwyrażenie zostanie obliczone na
null
, zostanie wywołaneNullPointerException
, a wyrażenie odniesienia do metody zostanie uzupełnione nagle .
O następującym kodzie:
Thread t = new Thread(s::toLowerCase); // <-- s is null, NullPointerException thrown here
t.setUncaughtExceptionHandler((t1, e) -> System.out.println("Exception!"));
Wyrażenie s
jest obliczane na null
i wyjątek jest rzucany dokładnie wtedy, gdy ta metoda-Referencja jest obliczana. Jednak w tym czasie nie dołączono żadnej obsługi wyjątków, ponieważ kod ten byłby wykonywany po.
Tak się nie dzieje w przypadku wyrażenia lambda, ponieważ lambda będzie oceniana bez wykonywania jej ciała. Z oceny Czasu Pracy Lambda Wyrażenia :
Ocena wyrażenia lambda różni się od wykonania ciała lambda.
Thread t = new Thread(() -> s.toLowerCase());
t.setUncaughtExceptionHandler((t1, e) -> System.out.println("Exception!"));
Nawet jeśli s
jest null
, wyrażenie lambda zostanie poprawnie utworzone. Następnie zostanie dołączony moduł obsługi wyjątków, wątek rozpocznie się, rzucając wyjątek, który zostanie przechwycony przez moduł obsługi.
Na marginesie, wydaje się, że zaćmienie Marsa.2 ma mały błąd dotyczący tego: nawet z method-reference, wywołuje wyjątek handler. Eclipse nie rzuca NullPointerException
w s::toLowerCase
kiedy powinno, więc odkłada wyjątek później, kiedy dodano obsługę wyjątków.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-07 14:20:35
Wow. Odkryłeś coś interesującego. Spójrzmy na:
Function<String, String> stringStringFunction = String::toLowerCase;
Zwraca nam funkcję, która przyjmuje parametr typu String
i zwraca inny String
, który jest małą literą parametru wejściowego. Jest to w pewnym sensie odpowiednik s.toLowerCase()
, gdzie s
jest parametrem wejściowym.
stringStringFunction(param) === param.toLowerCase()
Następna
Function<Locale, String> localeStringFunction = s::toLowerCase;
Jest funkcją od Locale
do String
. Jest to równoważne wywołaniu metody s.toLowerCase(Locale)
. Działa, pod maską, na 2 parametry: jeden to s
, a drugi to jakieś locale. Jeśli s
jest null
, to utworzenie tej funkcji rzuca NullPointerException
.
localeStringFunction(locale) === s.toLowerCase(locale)
Następny jest
Runnable r = () -> s.toLowerCase()
Która jest implementacją interfejsu Runnable
, który po wykonaniu wywoła metodę toLowerCase
na podanym łańcuchu s
.
Więc w Twoim przypadku
Thread t = new Thread(s::toLowerCase);
Próbuje utworzyć nowy Thread
przekazując do niego wynik wywołania s::toLowerCase
. Ale to rzuca NPE
na raz. Nawet przed rozpoczęciem wątku. I tak NPE
jest wrzucone do bieżącego wątku, a nie z wewnątrz wątku t
. Dlatego obsługa wyjątków nie jest wykonywana.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-24 12:32:08