问题
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 = Object::toString;
String s = toStringFunc.apply(o);
System.out.println(s);
}
}
This code will result in a NullPointerException
being thrown, reported at the line containing toStringFunc.apply(o)
.
This is a trivial example, so it's easy to see that o == null
, but how in general can we understand why this line of code would throw a NPE, as toStringFunc
, the only variable being dereferenced in that line, is not null.
回答1:
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
.
回答2:
when you do
toStringFunc.apply(o);
this is the same that
o.toString()
that's why you get a NullPointerException
so, you must ensure that your Object is not null.
来源:https://stackoverflow.com/questions/46898164/java-8-method-reference-to-class-instance-method-npe