Java NullPointerException而不是null(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

我在当前版本的Java8中发现了一个奇怪的行为。在我看来,下面的代码应该可以,但是JVM抛出了一个
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)