Java 中带盒装类型的三元条件的奇怪行为

Weird behaviour of ternary conditional with boxed types in Java

我的应用程序中有这段代码(简化版):

Object result;
if (check)
    result = new Integer(1);
else
    result = new Double(1.0);
System.out.println(result);
return result;

然后我决定将 if-else 语句重构为三元条件表达式,这样我的代码就更简洁了:

Object result = check ? new Integer(1) : new Double(1.0);
System.out.println(result);
return result;

事实证明,如果检查是 true 两个版本打印不同的结果:

1

或:

1.0

三元条件不是等价于对应的if-else吗?

if/else 和条件(三元)表达式不完全 等价。条件表达式的结果必须有一个类型。

您正在观察数字类型提升(或类型强制)的效果。

这是语言规范(参见 here)的摘录,来自描述条件表达式的 return 值的部分:

Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:

最后一个这样的案例是(我这里省略了其他案例):

Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.

这是与二进制数字提升相关的附加规范:

Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the following rules:

  • If either operand is of type double, the other is converted to double.

第一种情况(以下情况略)。 double 总是赢。

因此,无论条件表达式中第二个和第三个操作数的顺序如何,表达式的 return 类型都将提升为 double

除了@pb2q回答

你可以验证为

public class test {
    public static void main(String[] args) {
    Object result;
    Boolean check = true;

        if (check)
            result = new Integer(1);
        else
            result = new Double(1.0);
        System.out.println(result);
        result = check ? new Integer(2) : new Double(1.0);
        System.out.println(result);
    }
}

由于数字提升,它将打印 2.0 而不是 2

简而言之,三元运算符与涉及数字类型提升的其他运算符没有什么不同。

当您有类似 System.out.println(1 + 1.0) 的内容时,您希望它打印 2.0,因为输出中使用的操作数受数字类型提升的影响。

它与三元运算符完全相同:System.out.println(true ? 1 : 1.0) 将在执行与表达式为 1 + 1.0.[=15 时相同的数字类型提升后打印 1.0 =]

它以这种方式工作的原因非常简单:运算符结果的类型应该在编译时已知,而其实际结果在 运行 时确定。

简答

第一个示例显式输入为 Object,这会导致向上转换。

第二个示例隐式键入为 Double,这会导致数字扩大。


长答案

Object 的例子中,没有值的转换,只是一个向上转换,并打印了 1。

Object result;
if (1 == 1)
    result = new Integer(1);
else
    result = new Double(1.0);

如果您改为使用 Double 声明,它将是一个加宽并打印 1.0。

Double result;
if (1 == 1)
    result = new Integer(1);
else
    result = new Double(1.0);

这些都相当简单,因为有一个显式类型。

然而,三元表达式没有明确的类型,规则也很重要。

The type of a conditional expression is determined as follows:

  • If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.

  • If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.

  • If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.

  • Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:

    • If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the conditional expression is short.

    • If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression (§15.28) of type int whose value is representable in type T, then the type of the conditional expression is T.

    • If one of the operands is of type T, where T is Byte, Short, or Character, and the other operand is a constant expression (§15.28) of type int whose value is representable in the type U which is the result of applying unboxing conversion to T, then the type of the conditional expression is U.

    • Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands. Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).

  • Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25

数字 IntegerDouble 的“提升类型”是 Double

除了现有的答案之外,您还可以通过特定转换为您想要的类型来避免此问题:

Object result = args.length < 100 ? (Object)2 : (Object)1.0;

转换为 Object 将整数框为 Integer,将双精度框为 Double。 “:”两边的表达式都是 Object 类型,因此编译器不需要生成任何进一步的转换。