Java.Lang.Nullpointerexception Is Thrown Using a Method-Reference But Not a Lambda Expression

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

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.

NullPointerException when calling a method reference to an arbitrary object with null argument

I think you misunderstood the meaning of this line:

I i = Impl::get;

I is a functional interface that represents a method that takes an Impl and returns a boolean, whereas get is a method that takes no parameters and returns a boolean. How does this conversion work? Well, the compiler realises that get is an instance method, and to call it you must need a Impl object. Isn't that just like a function having a parameter before it is called?

So the compiler can happily infer that you meant:

I i = impl -> impl.get();

Now the cause of the NPE should be clear.

In general, all instance methods can be thought of as static methods that take one extra parameter, of type T where T is the declaring type of that instance method.

Java 8 method reference to class instance method NPE

Normally, you would look at the deepest stack trace entry to find out which variable has been dereferenced in the corresponding line.
You are right in that this is not possible here when the stack trace looks like

Exception in thread "main" java.lang.NullPointerException
at Playground.main(Playground.java:9)

The problem is that in this line in the main method, the actual dereferencing did not happen.
It happens within the invoked apply method whose implementation is part of a JRE generated class and whose stack frame has been omitted from the trace.

This was not always the case. It’s the result of JDK-8025636: Hide lambda proxy frames in stacktraces.
This change has been discussed in this Q&A as well.

The hiding works smoothly for lambda expressions, e.g. if you used

import java.util.function.Function;

public class Playground {
public static void main (String[] args) {
Object o = null;
System.out.println(o);
Function<Object, String> toStringFunc = obj -> obj.toString();
String s = toStringFunc.apply(o);
System.out.println(s);
}
}

instead, the stack trace looked like

Exception in thread "main" java.lang.NullPointerException
at Playground.lambda$main$0(Playground.java:8)
at Playground.main(Playground.java:9)

showing the exact place where the dereferencing happened while the irrelevant generated method mediating between the caller (main) and the callee (lambda$main$0) has been omitted.

Unfortunately, this doesn’t work that smooth for method references where the target method is invoked directly without the aid of another visible method.
This backfires especially in cases where the target method is not in the trace as the invocation itself failed, e.g. when the receiver instance is null.
Similar problems may occur when an attempt to unbox null happened in the generated code before or after the invocation of the target method.

One solution is to run the JVM with the options

-XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames to disable the hiding of stack frames. This may cause much longer stack traces as it also affects other binding code, e.g. for Reflection.
So you may only use this option when you have the suspicion that a certain exception did not happen at the reported place, but at a hidden frame. Using this option with you original code yields:

Exception in thread "main" java.lang.NullPointerException
at Playground$$Lambda$1/321001045.apply(<Unknown>:1000001)
at Playground.main(Playground.java:9)

The name of the class and method may vary, but it’s recognizable as generated code. From this stack trace, you can conclude that not a variable being dereferenced at main, line 9, but rather one of the arguments passed to the invocation must have been null.

Assert throws doesn't work for method reference

I am not really expert on interpretation of Java's method reference expressions, but my theory is that a reference to an instance method requires an access to the instance itself BEFORE the method handle, which represents the code to execute, can be created. That's why the NPE occurs before assertThrows(..) will be executed and thus the NPE cannot be caught by it.

Creating a method reference on a null-reference does not throw an exception

Is there a reason why it is possible to create method references on a null reference in Java?

It isn't, but apparently there's a bug in Eclipse in this regard (edit: which has since been fixed). According to the specification, and when you use the JDK's tools, it fails with an NPE on the Runnable fn = test::m; line.

Proof: http://ideone.com/APWXna (or compile and run it locally with javac and java rather than Eclipse)

Theory: From JLS §15.13.3:

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.

(My emphasis.)

Different behavior between lambda expression and method reference by instantiation

The timing of method reference expression evaluation differs from
which of lambda expressions.

With a method reference that has an expression (rather than a type) preceding the :: the subexpression is evaluated immediately and the result of evaluation is stored and reused then.

So here :

new Instance()::set

new Instance() is evaluated a single time.

From 15.12.4. Run-Time Evaluation of Method Invocation (emphasis is mine) :

The timing of method reference expression evaluation is more complex
than that of lambda expressions (§15.27.4). When a method reference
expression has an expression (rather than a type) preceding the ::
separator, that subexpression is evaluated immediately. The result of
evaluation is stored until the method of the corresponding functional
interface type is invoked; at that point, the result is used as the
target reference for the invocation. This means the expression
preceding the :: separator is evaluated only when the program
encounters the method reference expression, and is not re-evaluated on
subsequent invocations on the functional interface type
.

Difference between method reference and lambda

The lambda is evaluated when it's called: if you called map.get("key1").someAction() you would get a NPE.

The method reference is evaluated at creation time, i.e. when you first write object::notify, which throws a NPE straight away.

In particular, the JLS 15.13.3 states:

Evaluation of a method reference expression is distinct from invocation of the method itself.

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.

What is the reason behind null checks in method reference expression evaluation?

The reason here is the fact that when you are creating non static method reference, it must have access to this. When you are trying to create reference to null object, there is no this anywhere, that is why it should fail on this step, instead of failing somewhere further in the code when it is used for the first time.

Imagine in one place you get the object, save it's method reference somewhere, and than use it in completely different part of code. You'll get an NPE not in the place where the error was made, but many lines of code away.



Related Topics



Leave a reply



Submit