Krz*_*ski 44 java lambda nullpointerexception java-8 method-reference
我注意到使用Java 8方法引用的未处理异常有些奇怪.这是我的代码,使用lambda表达式() -> 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();
}
}
Run Code Online (Sandbox Code Playgroud)
它打印"Exception",所以它工作正常.但是当我Thread t改为使用方法引用时(甚至IntelliJ建议):
Thread t = new Thread(s::toLowerCase);
Run Code Online (Sandbox Code Playgroud)
异常没有被捕获:
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)
Run Code Online (Sandbox Code Playgroud)
有人能解释一下这里发生了什么吗?
Tun*_*aki 54
此行为依赖于方法引用和lambda表达式的评估过程之间的细微差别.
首先,如果方法引用表达式以ExpressionName或Primary开头,则计算此子表达式.如果子表达式求值为
null,NullPointerException则引发a,并且方法引用表达式突然完成.
使用以下代码:
Thread t = new Thread(s::toLowerCase); // <-- s is null, NullPointerException thrown here
t.setUncaughtExceptionHandler((t1, e) -> System.out.println("Exception!"));
Run Code Online (Sandbox Code Playgroud)
表达式s被计算到,null并且在评估该方法引用时将完全抛出异常.但是,那时没有附加异常处理程序,因为此代码将在之后执行.
在lambda表达式的情况下不会发生这种情况,因为lambda将在不执行其主体的情况下进行计算.从Lambda表达式的运行时评估:
lambda表达式的评估不同于lambda体的执行.
Thread t = new Thread(() -> s.toLowerCase());
t.setUncaughtExceptionHandler((t1, e) -> System.out.println("Exception!"));
Run Code Online (Sandbox Code Playgroud)
即使s是null,也将正确创建lambda表达式.然后将附加异常处理程序,线程将启动,抛出异常,将由处理程序捕获.
作为一个侧面说明,似乎Eclipse的Mars.2有这方面一个小bug:即使有方法参考,它调用异常处理程序.Eclipse NullPointerException在s::toLowerCase应该的时候不会抛出它,因此在添加异常处理程序之后推迟异常.
哇.你发现了一些有趣的东西.我们来看看以下内容:
Function<String, String> stringStringFunction = String::toLowerCase;
Run Code Online (Sandbox Code Playgroud)
这返回一个函数,它接受on类型的参数String并返回另一个函数String,它是输入参数的小写.这有点相当于s.toLowerCase(),其中s是输入参数.
stringStringFunction(param) === param.toLowerCase()
Run Code Online (Sandbox Code Playgroud)
下一个
Function<Locale, String> localeStringFunction = s::toLowerCase;
Run Code Online (Sandbox Code Playgroud)
是从函数Locale到String.这相当于s.toLowerCase(Locale)方法调用.它在引擎盖下有两个参数:一个是s另一个是某个区域设置.如果s是null,那么这个函数创建抛出一个NullPointerException.
localeStringFunction(locale) === s.toLowerCase(locale)
Run Code Online (Sandbox Code Playgroud)
接下来是
Runnable r = () -> s.toLowerCase()
Run Code Online (Sandbox Code Playgroud)
这是一个Runnable接口的实现,在执行时,将调用toLowerCase给定字符串上的方法s.
所以在你的情况下
Thread t = new Thread(s::toLowerCase);
Run Code Online (Sandbox Code Playgroud)
尝试创建一个新的Thread传递调用的结果s::toLowerCase.但这会NPE立刻引发.甚至在线程启动之前.因此NPE抛出当前线程,而不是内部线程t.这就是您的异常处理程序未执行的原因.
| 归档时间: |
|
| 查看次数: |
3597 次 |
| 最近记录: |