Java 8 method reference to class instance method NPE

依然范特西╮ 提交于 2020-01-30 13:27:12

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!