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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…