我在当前版本的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,混合模式)
答案 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
),因此false
和null
都可以插入其中。
然后整个表达是:
false ? false : (Boolean expression)
然后,再次根据JLS §15.25:
如果第二个和第三个操作数之一是原始类型T,和 另一种的类型是应用拳击转换的结果 (§5.1.7)到T,那么条件表达式的类型是T.
所以,第一个参数是原始类型boolean
(规范中为T
),另一个的类型是装箱T
(Boolean
),所以整个表达式的类型是boolean
。
然后,在运行时,嵌入式表达式的计算结果为null
,它会自动取消装箱到boolean
,从而导致NPE。