NullPointerException而不是null(JVM Bug?)

ste*_*fen 7 java lambda jvm ternary-operator

我在当前版本的Java 8中发现了一个奇怪的行为.在我看来,下面的代码应该没问题,但JVM会抛出NullPointerException:

Supplier<Object> s = () -> false ? false : false ? false : null;
s.get(); // expected: null, actual: NullPointerException
Run Code Online (Sandbox Code Playgroud)

没关系,它是什么样的lambda表达式(与之相同java.util.function.Function)或使用什么泛型类型.也可以用更有意义的表达来代替false ? :.上面的例子非常简短.这是一个更加丰富多彩的例子:

Function<String, Boolean> f = s -> s.equals("0") ? false : s.equals("1") ? true : null;
f.apply("0"); // false
f.apply("1"); // true
f.apply("2"); // expected: null, actual: NullPointerException
Run Code Online (Sandbox Code Playgroud)

但是这些代码块运行良好:

Supplier<Object> s = () -> null;
s.get(); // null
Run Code Online (Sandbox Code Playgroud)

Supplier<Object> s = () -> false ? false : null;
s.get(); // null
Run Code Online (Sandbox Code Playgroud)

或者有功能:

Function<String, Boolean> f = s -> {
    if (s.equals("0")) return false;
    else if (s.equals("1")) return true;
    else return null;
};
f.apply("0"); // false
f.apply("1"); // true
f.apply("2"); // null
Run Code Online (Sandbox Code Playgroud)

我测试了两个Java版本:

~# java -version

openjdk版本"1.8.0_66-internal"OpenJDK运行时环境(build 1.8.0_66-internal-b01)OpenJDK 64位服务器VM(内置25.66-b01,混合模式)

C:\>java -version

java版"1.8.0_51"Java(TM)SE运行时环境(版本1.8.0_51-b16)Java HotSpot(TM)64位服务器VM(版本25.51-b03,混合模式)

Dra*_*vic 8

这与lambda表达式无关; 在这种情况下boolean,简单地说三元运算符的返回类型是,因此将使用自动拆箱.

NPE也被抛到这里:

public class Main {

    private static Object method() {
        return false ? false : false ? false : null;
    }

    public static void main(String[] args) {
        System.out.println(method());
    }
}
Run Code Online (Sandbox Code Playgroud)

那么,到底发生了什么?

首先,false ? false : null根据JLS§15.25评估'embedded'expression():

条件运算符在语法上是右关联的(它从右到左分组).因此,a?b:c?d:e?f:g表示与a b相同:(c?d:(e?f:g)).

类型嵌入表达的是Boolean(盒装boolean),这样既falsenull可以放入它.

然后整个表达是:

false ? false : (Boolean expression)
Run Code Online (Sandbox Code Playgroud)

然后,再次根据JLS§15.25:

如果第二个和第三个操作数之一是原始类型T,而另一个操作数的类型是将装箱转换(第5.1.7节)应用于T的结果,那么条件表达式的类型是T.

因此,第一个参数是原始类型boolean(T在规范中),另一个的类型是boxed T(Boolean),因此整个表达式的类型是boolean.

然后,在运行时,嵌入式表达式计算null自动取消装箱到哪个boolean,从而导致NPE.