Java 8 方法参考 class 实例方法 NPE
Java 8 method reference to class instance method NPE
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);
}
}
此代码将导致 NullPointerException
被抛出,在包含 toStringFunc.apply(o)
.
的行报告
这是一个简单的例子,所以很容易看出 o == null
,但是我们一般如何理解为什么这行代码会抛出 NPE,因为 toStringFunc
,唯一的变量在该行中被取消引用,不为空。
当你
toStringFunc.apply(o);
这与
相同
o.toString()
这就是为什么你得到 NullPointerException
因此,您必须确保您的对象不为空。
通常,您会查看最深的堆栈跟踪条目,以找出在相应行中取消引用了哪个变量。
你是对的,当堆栈跟踪看起来像
时,这是不可能的
Exception in thread "main" java.lang.NullPointerException
at Playground.main(Playground.java:9)
问题是在 main
方法的这一行中,实际取消引用并没有发生。
它发生在调用的 apply
方法中,该方法的实现是生成的 JRE 的一部分 class 并且其堆栈帧已从跟踪中省略。
情况并非总是如此。这是 JDK-8025636: Hide lambda proxy frames in stacktraces 的结果。
中也讨论了此更改。
对于 lambda 表达式,隐藏工作很顺利,例如如果你使用
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);
}
}
相反,堆栈跟踪看起来像
Exception in thread "main" java.lang.NullPointerException
at Playground.lambda$main[=12=](Playground.java:8)
at Playground.main(Playground.java:9)
显示取消引用发生的确切位置,同时省略了调用者 (main
) 和被调用者 (lambda$main[=17=]
) 之间不相关的生成方法。
不幸的是,对于直接调用目标方法而不借助其他可见方法的方法引用,这并不顺利。
这适得其反,尤其是在目标方法不在跟踪中的情况下,因为调用本身失败了,例如当接收者实例为 null
时。
当在调用目标方法之前或之后在生成的代码中尝试取消装箱 null
时,可能会出现类似的问题。
一个解决方案是 运行 带有选项的 JVM
-XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames
禁用堆栈帧的隐藏。这可能会导致更长的堆栈跟踪,因为它还会影响其他绑定代码,例如进行反思。
所以只有当你怀疑某个异常不是发生在报告的地方,而是发生在隐藏的框架时,你才可以使用这个选项。将此选项与原始代码一起使用会产生:
Exception in thread "main" java.lang.NullPointerException
at Playground$$Lambda/321001045.apply(<Unknown>:1000001)
at Playground.main(Playground.java:9)
class 和方法的名称可能有所不同,但可以识别为生成的代码。从此堆栈跟踪中,您可以得出结论,不是在第 9 行 main
取消引用的变量,而是传递给调用的参数之一必须是 null
.
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);
}
}
此代码将导致 NullPointerException
被抛出,在包含 toStringFunc.apply(o)
.
这是一个简单的例子,所以很容易看出 o == null
,但是我们一般如何理解为什么这行代码会抛出 NPE,因为 toStringFunc
,唯一的变量在该行中被取消引用,不为空。
当你
toStringFunc.apply(o);
这与
相同o.toString()
这就是为什么你得到 NullPointerException
因此,您必须确保您的对象不为空。
通常,您会查看最深的堆栈跟踪条目,以找出在相应行中取消引用了哪个变量。 你是对的,当堆栈跟踪看起来像
时,这是不可能的Exception in thread "main" java.lang.NullPointerException
at Playground.main(Playground.java:9)
问题是在 main
方法的这一行中,实际取消引用并没有发生。
它发生在调用的 apply
方法中,该方法的实现是生成的 JRE 的一部分 class 并且其堆栈帧已从跟踪中省略。
情况并非总是如此。这是 JDK-8025636: Hide lambda proxy frames in stacktraces 的结果。
对于 lambda 表达式,隐藏工作很顺利,例如如果你使用
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);
}
}
相反,堆栈跟踪看起来像
Exception in thread "main" java.lang.NullPointerException
at Playground.lambda$main[=12=](Playground.java:8)
at Playground.main(Playground.java:9)
显示取消引用发生的确切位置,同时省略了调用者 (main
) 和被调用者 (lambda$main[=17=]
) 之间不相关的生成方法。
不幸的是,对于直接调用目标方法而不借助其他可见方法的方法引用,这并不顺利。
这适得其反,尤其是在目标方法不在跟踪中的情况下,因为调用本身失败了,例如当接收者实例为 null
时。
当在调用目标方法之前或之后在生成的代码中尝试取消装箱 null
时,可能会出现类似的问题。
一个解决方案是 运行 带有选项的 JVM
-XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames
禁用堆栈帧的隐藏。这可能会导致更长的堆栈跟踪,因为它还会影响其他绑定代码,例如进行反思。
所以只有当你怀疑某个异常不是发生在报告的地方,而是发生在隐藏的框架时,你才可以使用这个选项。将此选项与原始代码一起使用会产生:
Exception in thread "main" java.lang.NullPointerException
at Playground$$Lambda/321001045.apply(<Unknown>:1000001)
at Playground.main(Playground.java:9)
class 和方法的名称可能有所不同,但可以识别为生成的代码。从此堆栈跟踪中,您可以得出结论,不是在第 9 行 main
取消引用的变量,而是传递给调用的参数之一必须是 null
.