使用方法引用而不是 lambda 表达式抛出 java.lang.NullPointerException [英] java.lang.NullPointerException is thrown using a method-reference but not a lambda expression

查看:35
本文介绍了使用方法引用而不是 lambda 表达式抛出 java.lang.NullPointerException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我注意到有关使用 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();
    }
}

它打印异常",所以它工作正常.但是当我更改 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, a NullPointerException 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!"));

即使 snull,也会正确创建 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆