NullPointerException而不是null(JVM Bug?)

时间:2015-08-12 16:00:02

标签: java lambda jvm ternary-operator

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

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

无关紧要,它是什么样的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

然而,这些代码片段运行良好:

Supplier<Object> s = () -> null;
s.get(); // null

Supplier<Object> s = () -> false ? false : null;
s.get(); // null

或使用功能:

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

我测试了两个Java版本:

~# java -version

  

openjdk version“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,混合模式)

1 个答案:

答案 0 :(得分:7)

这与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());
    }
}

那么,到底发生了什么?

首先,'嵌入'表达式(false ? false : null)根据JLS §15.25进行评估:

  

条件运算符在语法上是右关联的(它是组合的   右到左)。因此,a?b:c?d:e?f:g表示与...相同   A B:(2 C d:(E F:G))。

嵌入式表达式的类型为Boolean(加框boolean),因此falsenull都可以插入其中。

然后整个表达是:

false ? false : (Boolean expression)

然后,再次根据JLS §15.25

  

如果第二个和第三个操作数之一是原始类型T,和   另一种的类型是应用拳击转换的结果   (§5.1.7)到T,那么条件表达式的类型是T.

所以,第一个参数是原始类型boolean(规范中为T),另一个的类型是装箱TBoolean),所以整个表达式的类型是boolean

然后,在运行时,嵌入式表达式的计算结果为null,它会自动取消装箱到boolean,从而导致NPE。