使用方法引用而不是lambda表达式抛出java.lang.NullPointerException [英] java.lang.NullPointerException is thrown using a method-reference but not a lambda expression
问题描述
我注意到使用Java 8方法引用的未处理异常有些奇怪。这是我的代码,使用lambda表达式() - > s.toLowerCase()
:
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();
}
}
它打印Exception,所以它工作正常。但是,当我更改 Thread t
以使用方法引用时(甚至IntelliJ建议):
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);
未捕获异常:
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?
推荐答案
此行为依赖于方法引用和lambda表达式的评估过程之间的细微差别。
This behaviour relies on a subtle difference between the evaluation process of method-references and lambda expressions.
来自JLS 方法参考的运行时评估:
首先,如果方法引用表达式以ExpressionName或Primary开头,则计算此子表达式。 如果子表达式求值为
null
,则会引发NullPointerException
,并且方法引用表达式突然完成。
First, if the method reference expression begins with an ExpressionName or a Primary, this subexpression is evaluated. If the subexpression evaluates to
null
, aNullPointerException
is raised, and the method reference expression completes abruptly.
使用以下代码:
Thread t = new Thread(s::toLowerCase); // <-- s is null, NullPointerException thrown here
t.setUncaughtExceptionHandler((t1, e) -> System.out.println("Exception!"));
表达式 s
评估为 null
并且在评估该方法引用时会完全抛出异常。但是,那时没有附加异常处理程序,因为此代码将在执行之后执行。
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.
在lambda表达式的情况下不会发生这种情况,因为lambda将在不执行其正文的情况下进行评估。来自 Lambda的运行时评估表达式:
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:
评估lambda表达式与执行lambda体不同。
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!"));
即使 s
是 null
,将正确创建lambda表达式。然后将附加异常处理程序,线程将启动,抛出异常,将由处理程序捕获。
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.
作为旁注,Eclipse Mars.2似乎有一个小错误:即使使用方法引用,它也会调用异常处理程序。 Eclipse不应该在 s :: toLowerCase
中抛出 NullPointerException
,因此应该在以后延迟异常,添加了异常处理程序。
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.
这篇关于使用方法引用而不是lambda表达式抛出java.lang.NullPointerException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!