【问题标题】:Weird behaviour of ternary conditional with boxed types in Java [duplicate]Java中带有盒装类型的三元条件的奇怪行为[重复]
【发布时间】:2015-07-28 12:51:50
【问题描述】:

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

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;

原来如果check是true,两个版本打印出不同的结果:

1

或:

1.0

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

【问题讨论】:

标签: java ternary-operator


【解决方案1】:

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

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

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

否则,如果第二个和第三个操作数具有可转换(第 5.1.8 节)为数值类型的类型,则有以下几种情况:

最后一种情况是(我在这里省略了其他情况):

否则,二进制数值提升(第 5.6.2 节)将应用于操作数类型,条件表达式的类型是第二个和第三个操作数的提升类型。

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

加宽原语转换(第 5.1.2 节)适用于转换以下规则中指定的一个或两个操作数:

  • 如果任一操作数为 double 类型,则另一个将转换为 double。

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

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

【讨论】:

  • 我认为你真的必须研究二进制数字提升的规则才能完全理解发生了什么。这可以执行到 int 和 double 的拆箱转换,然后将 int 扩大到 double。就像你说new Integer(1) + new Double(1.0)一样。
【解决方案2】:

简单地说,三元运算符与其他涉及数值类型提升的运算符没有什么不同。

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

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

这样做的原因很简单:运算符结果的类型应该在编译时知道,而其实际结果在运行时确定。

【讨论】:

    【解决方案3】:

    简答

    第一个示例明确键入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);
    

    这些都相当简单,因为有一个明确的类型。

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

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

    • 如果第二个和第三个操作数的类型相同(可能是空类型),那么就是条件表达式的类型。

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

    • 如果第二个和第三个操作数之一是空类型,另一个是引用类型,那么条件表达式的类型就是那个引用类型。

    • 否则,如果第二个和第三个操作数具有可转换(第 5.1.8 节)为数字类型的类型,则有几种情况:

      • 如果其中一个操作数是byte或Byte类型,另一个是short或Short类型,则条件表达式的类型为short。

      • 如果其中一个操作数是 T 类型,其中 T 是 byte、short 或 char,而另一个操作数是 int 类型的常量表达式(第 15.28 节),其值可在类型 T 中表示,则条件表达式是T。

      • 如果其中一个操作数是 T 类型,其中 T 是 Byte、Short 或 Character,而另一个操作数是 int 类型的常量表达式(第 15.28 节),其值可以在作为结果的 U 类型中表示对T进行拆箱转换,则条件表达式的类型为U。

      • 否则,二进制数值提升(第 5.6.2 节)将应用于操作数类型,条件表达式的类型是第二个和第三个操作数的提升类型。请注意,二进制数值提升会执行值集转换(第 5.1.13 节),并且可能会执行拆箱转换(第 5.1.8 节)。

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

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

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

    【讨论】:

      【解决方案4】:

      除了@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

      【讨论】:

      • 这不是和问题中的代码一样吗?
      • 这是唯一的区别 result = check ? new Integer(2) : new Double(1.0); 它将打印 2.0 而不是 2。
      • 那么 Q&A 中增加了什么?
      • 因为new Integer(1) 而不是因为new Double(1.0) 而显示有问题的1.0 打印件
      • 我明白你在说什么:结果是由于数字提升,而不是因为由于某种原因条件被评估为false。 (也许您应该将其添加到您的答案中?)
      【解决方案5】:

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

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

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

      【讨论】:

        猜你喜欢
        • 2016-10-31
        • 2015-02-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-14
        • 1970-01-01
        • 2021-03-29
        • 1970-01-01
        相关资源
        最近更新 更多