java.lang.NullPointerException is thrown using a method-reference but not a lambda expression
Solution 1:
This behaviour relies on a subtle difference between the evaluation process of method-references and lambda expressions.
From the JLS Run-Time Evaluation of Method References:
First, if the method reference expression begins with an ExpressionName or a Primary, this subexpression is evaluated. If the subexpression evaluates to
null
, aNullPointerException
is raised, and the method reference expression completes abruptly.
With the following code:
Thread t = new Thread(s::toLowerCase); // <-- s is null, NullPointerException thrown here
t.setUncaughtExceptionHandler((t1, e) -> System.out.println("Exception!"));
the expression s
is evaluated to null
and an exception is thrown exactly when that method-reference is evaluated. However, at that time, no exception handler was attached, since this code would be executed after.
This doesn't happen in the case of a lambda expression, because the lambda will be evaluated without its body being executed. From Run-Time Evaluation of Lambda Expressions:
Evaluation of a lambda expression is distinct from execution of the lambda body.
Thread t = new Thread(() -> s.toLowerCase());
t.setUncaughtExceptionHandler((t1, e) -> System.out.println("Exception!"));
Even if s
is null
, the lambda expression will be correctly created. Then the exception handler will be attached, the thread will start, throwing an exception, that will be caught by the handler.
As a side-note, it seems Eclipse Mars.2 has a small bug regarding this: even with the method-reference, it invokes the exception handler. Eclipse isn't throwing a NullPointerException
at s::toLowerCase
when it should, thus deferring the exception later on, when the exception handler was added.
Solution 2:
Wow. You have uncovered something interesting. Let us take a look at the following:
Function<String, String> stringStringFunction = String::toLowerCase;
This returns us a function which accepts on parameter of type String
and returns another String
, which is a lowercase of the input parameter. This is somewhat equivalent to the s.toLowerCase()
, where s
is the input parameter.
stringStringFunction(param) === param.toLowerCase()
Next
Function<Locale, String> localeStringFunction = s::toLowerCase;
is a function from Locale
to String
. This is equivalent to s.toLowerCase(Locale)
method invocation. It works, under the hood, on 2 parameters: one is s
and another is some locale. If s
is null
, then this function creation throws a NullPointerException
.
localeStringFunction(locale) === s.toLowerCase(locale)
Next is
Runnable r = () -> s.toLowerCase()
Which is an implementation of Runnable
interface which, when executed, will call method toLowerCase
on given string s
.
So in your case
Thread t = new Thread(s::toLowerCase);
tries to create a new Thread
passing the result of the invocation of s::toLowerCase
to it. But this throws a NPE
at once. Even before thread is started. And so NPE
is thrown in your current thread, not from inside thread t
. This is why your exception handler is not executed.