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, a NullPointerException 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.