18

这段丑陋的代码确实可以编译,但如果s == null

public static boolean isNullOrEmpty(String s)
{
    return s != null ? s.isEmpty() : null;
}

虽然这不是(如预期的那样):

public static boolean isNullOrEmpty(String s)
{
    if(s != null)
        return s.isEmpty();
    else
        return null;
}

我知道它们都明显是错误的,但是当我在我们的源代码中找到第一段代码时,我很惊讶它确实编译了。

编辑:这是 Java 7 中 JLS 的相关部分。我猜第一个语句会适用,但粗体的语句会适用。

15.25 条件运算符?:

[...]

条件表达式的类型确定如下:

[...]

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

[...]

  • 否则,第二个和第三个操作数分别是 S1 和 S2 类型。令 T1 为对 S1 应用装箱转换产生的类型,令 T2 为对 S2 应用装箱转换产生的类型。条件表达式的类型是将捕获转换 (§5.1.10) 应用于 lub(T1, T2) (§15.12.2.7) 的结果。
4

3 回答 3

16

第一个有一个三元运算符,其结果类型为Boolean. NPE 正在将 a 转换null为 a boolean

它实际上是这样的:

Boolean temp = s != null ? s.isEmpty() : null; //no problems here
return temp; //crash when temp==null

第二个是尝试返回错误的类型(对象而不是原始类型) - 因此无法编译。

于 2012-11-29T12:36:46.600 回答
0

类似于JAVA 中的三元运算符

三元运算符使用 JLS 中引用的规则进行自动装箱:

拳击转换

此规则是必要的,因为条件运算符(第 15.25 节)将装箱转换应用于其操作数的类型,并在进一步的计算中使用结果。

问题在于Java中的自动装箱。问题是由于第二个操作数不是第三个。

public static boolean isNullOrEmpty(String s)
    {
        return s != null ? null : null;
    }

此代码不会编译

条件运算符的 JLS

于 2012-11-29T12:49:12.547 回答
0

三元运算符需要为两个操作数找出最合适的返回类型。

因此,它允许对返回类型进行一些“修饰”。

如您的第一个代码片段所示:

public static boolean isNullOrEmpty(String s)
{
    return s != null ? s.isEmpty() : null;
}

s.empty()返回原语boolean,而第三个操作数返回null. 所以最具体的常见返回类型是Boolean. 编译器工作就像将行替换为:

return s != null ? s.isEmpty() : (Boolean)null; 

返回类型方法需要一个boolean原语,所以编译器说:“酷,我只需要拆箱我的结果!”。不幸的是,null它不是不可拆箱的,并且会导致丑陋的 NPE。

使用您的第二个片段:

public static boolean isNullOrEmpty(String s)
{
    if(s != null)
        return s.isEmpty();
    else
        return null;
}

由于编译器没有链接此代码中的两个返回语句=> 对它来说可能是一项太难的工作,因此不会对预返回类型进行额外的“抛光”。

因此,在这种情况下,代码甚至不编译是合乎逻辑的,因为null它与Boolean! 因此,不会发生任何类型的演员表。

要编译它,它应该写成:

public static boolean isNullOrEmpty(String s)
    {
        if(s != null)
            return s.isEmpty();
        else
            return (Boolean)null;
    }

但不会阻止著名的 NPE 在尝试拆箱时发生;)

于 2012-11-29T13:44:11.743 回答