Java NullPointerException而不是null(JVM错误?)
我在当前版本的Java8中发现了一个奇怪的行为。在我看来,下面的代码应该可以,但是JVM抛出了一个Java NullPointerException而不是null(JVM错误?),java,lambda,jvm,ternary-operator,Java,Lambda,Jvm,Ternary Operator,我在当前版本的Java8中发现了一个奇怪的行为。在我看来,下面的代码应该可以,但是JVM抛出了一个NullPointerException: Supplier<Object> s = () -> false ? false : false ? false : null; s.get(); // expected: null, actual: NullPointerException 但是,这些代码段运行良好: Supplier<Object> s = () -&g
NullPointerException
:
Supplier<Object> s = () -> false ? false : false ? false : null;
s.get(); // expected: null, actual: NullPointerException
但是,这些代码段运行良好:
Supplier<Object> s = () -> null;
s.get(); // null
供应商s=()->空;
s、 get();//无效的
及
suppliers=()->false?false:null;
s、 get();//无效的
或具有以下功能:
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
函数f=s->{
如果(s.equals(“0”))返回false;
否则,如果(s.equals(“1”))返回true;
否则返回null;
};
f、 应用(“0”);//假的
f、 应用(“1”);//真的
f、 应用(“2”);//无效的
我使用两个Java版本进行了测试:
~#java-版本
openjdk版本“1.8.0_66-内部”
OpenJDK运行时环境(build 1.8.0_66-internal-b01)
OpenJDK 64位服务器虚拟机(构建25.66-b01,混合模式)
C:\>java-version
java版本“1.8.0_51”
Java(TM)SE运行时环境(build 1.8.0_51-b16)
Java HotSpot(TM)64位服务器虚拟机(构建25.51-b03,混合模式)
这与lambda表达式无关;简单地说,在这种情况下,三元运算符的返回类型是
布尔值
,因此将使用自动取消装箱
NPE也在这里抛出:
public class Main {
private static Object method() {
return false ? false : false ? false : null;
}
public static void main(String[] args) {
System.out.println(method());
}
}
那么,这里到底发生了什么
首先,根据JLS评估“嵌入式”表达式(false?false:null
):
条件运算符在语法上是右关联的(it)
从右到左)。因此,a?b:c?d:e?f:g的意思与
a?b:(c?d:(e?f:g))
嵌入表达式的类型是Boolean
(装箱Boolean
),因此false
和null
都可以放入其中
那么整个表达就是:
false ? false : (Boolean expression)
然后,同样根据JLS:
如果第二个和第三个操作数之一为基元类型T,并且
另一个的类型是应用装箱转换的结果
(§5.1.7)至T,则条件表达式的类型为T
因此,第一个参数是基本类型boolean
(T
,在规范中),另一个参数是装箱的T
(boolean
),因此整个表达式的类型是boolean
然后,在运行时,嵌入表达式的计算结果为
null
,该值自动取消绑定为布尔值
,从而导致NPE。这与对象a=false有何不同?假:假?false:null
也会产生一个NullPointerException
?它出现在Java 7和Java 8(OpenJDK)中。@bayou.io如何?Lambdas没有添加任何内容。“问题”是三元表达式。我认为这个问题的答案是将null
解装箱为boolean
值,因为使用两个?:
运算符时具有绑定/优先级。重复的帖子也说了同样多的内容(可能没有那么多关于绑定的内容,但暗示了它)。关于这一点,没有必要写10篇不同的帖子。如果你有更好的副本,我很乐意重新打开,我们可以重新关闭它。这个问题已经被回答了很多次了。@steffen-很明显,在某个地方有拆箱的方法。但原因不明;或者它是合法的还是漏洞。我敢打赌,即使是语言设计师也不能马上回答这个问题。java8确实使?:
更加复杂。请参阅我的分析,OP的代码在Java8中,因此我们必须考虑目标键入<代码>对象< /代码>,并且我们必须分析它是否会影响操作数;为什么取消装箱而不是装箱。那么为什么为false?假:假?Boolean.FALSE:null代码>仍然抛出一个NPE。嵌套表达式的第二个操作数是布尔值
,因此不会应用装箱转换(§5.1.7)。外部表达式将是false?false:(布尔表达式)
。JLS§15.25中的下一行读取,如果第二个和第三个操作数中的一个为null类型,而另一个操作数的类型为引用类型,则条件表达式的类型为该引用类型。
确定,但最后将等于false?false:(布尔值)null
false?假:假?Boolean.FALSE:null
是相同的。嵌入的三元表达式的计算结果为Boolean
,整个表达式返回Boolean
,因为它是上面引用的基元类型T
。对于您引用的段落,表达式中没有出现这种情况,false
不是引用类型,并且(Boolean)null
不是null类型。引用的JLS语句适用于false?假:假?Boolean.FALSE:null
(2,3个运算符中的一个为null
,另一个为引用类型)。因此,内部表达式的类型是引用类型(Boolean
,无自动装箱)。因此,整个表达式是boolean?布尔值:布尔值
,表示NPE。但是对于最初的问题,您的解释是完美的(+1),至少bayou.io没有找到Java8的细节。
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 : (Boolean expression)