java.lang.NullPointerException is thrown using a method-reference but not a lambda expression

JavaLambdaNullpointerexceptionJava 8Method Reference

Java Problem Overview


I've noticed something weird about unhandled exceptions using Java 8 method reference. This is my code, using the lambda expression () -> 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();
    }
}

It prints "Exception", so it works fine. But when I change Thread t to use a method-reference (even IntelliJ suggests that):

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

the exception is not being caught:

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)

Can someone explain what is going on here?

Java Solutions


Solution 1 - Java

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 - Java

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKrzysztof MajewskiView Question on Stackoverflow
Solution 1 - JavaTunakiView Answer on Stackoverflow
Solution 2 - JavaNikemView Answer on Stackoverflow