三目运算符为什么会意外地转换整数?

47

我在某处看到有讨论说下面的代码会导致obj成为一个Double,但是它从左侧打印出200.0

我曾经看到有人讨论以下代码,表示obj将被视为Double类型,但它会从左边打印出200.0

Object obj = true ? new Integer(200) : new Double(0.0);

System.out.println(obj);

结果:200.0


但是,如果在右边放一个不同的对象,例如BigDecimal,那么obj的类型是正确的Integer

Object obj = true ? new Integer(200) : new BigDecimal(0.0);

System.out.println(obj);

结果:200


我推测这是与将左侧转换为 double 相关,就像在 integer/double 的比较和计算中发生的那样,但在这里左侧和右侧没有以这种方式交互。

为什么会这样?


我不熟悉Java,但是正确的问题应该是“三元条件表达式的类型是什么?” - Kerrek SB
不得不承认这是相当混乱的东西。当我在NetBeans中运行它并且obj.getClass()显示为Double时,真的让我感到惊讶。这是怎么回事啊,伙计。 - G_H
3个回答

43

你需要阅读Java语言规范的第15.25节

特别是:

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

  • 如果其中一个操作数的类型为byte或Byte,另一个操作数的类型为short或Short,则条件表达式的类型为short。
  • 如果其中一个操作数的类型为T,其中T为byte、short或char,另一个操作数是类型为int的常量表达式,其值可表示为类型T,则条件表达式的类型为T。
  • 如果其中一个操作数的类型为Byte,另一个操作数是类型为byte的常量表达式,其值可表示为byte类型,则条件表达式的类型为byte。
  • 如果其中一个操作数的类型为Short,另一个操作数是类型为short的常量表达式,其值可表示为short类型,则条件表达式的类型为short。
  • 如果其中一个操作数的类型为Character,另一个操作数是类型为int的常量表达式,其值可表示为char类型,则条件表达式的类型为char。
  • 否则,将应用二进制数字提升(§5.6.2)到操作数类型,并且条件表达式的类型是第二个和第三个操作数的提升类型。请注意,二进制数字推广执行取消装箱转换(§5.1.8)和值设置转换(§5.1.13)。

所以应用二进制数值提升,它开始于:

当运算符将二进制数值提升应用于一对操作数时,每个操作数都必须表示可转换为数值类型的值,按顺序使用扩展转换(§5.1.2)进行转换操作:

  • 如果任何操作数是引用类型,则执行拆箱转换(§5.1.8)。然后:
  • 如果任一操作数为double类型,则将另一个操作数转换为double。

这正是此处发生的情况 - 参数类型分别转换为intdouble,第二个操作数(原始表达式中的第三个操作数)现在是double类型,因此总结果类型是double


2
Gah,Jon Skeet比我先了 :) +1 - Vivin Paliath
1
@VivinPaliath 我也是,但没关系,因为我正准备从我们之前关于三元运算符的另一个问题中窃取他的引用;-) - Voo
同感...我有点困惑为什么这被认为是必要的。从纯粹的功能角度来看,这可能很有用,但是考虑到反射和 Class 检查经常存在,这似乎在某些情况下是一个巨大的陷阱。 - G_H
1
@HXCaine:如果是相同类型但是原始类型,你希望它做什么?请记住,表达式的类型与赋值部分是分开的。 - Jon Skeet
3
是的。编译器决定类型,然后考虑是否可转换为分配目标。请注意,泛型类型推断是其中一个反例,但它是我能想到的唯一一个 :) - Jon Skeet
显示剩余2条评论

3

条件运算符中的数字转换 ? :

在条件运算符a?b:c中,如果bc都是不同的数字类型,则在编译时应用以下转换规则使它们的类型相等

  • 将类型转换为其对应的原始类型,即所谓的取消装箱

  • 如果一个操作数是可表示为另一种类型的常量int(未取消装箱之前不是Integer),则将int操作数转换为另一种类型。

  • 否则,较小的类型被转换为下一个更大的类型,直到两个操作数具有相同的类型。转换顺序如下:
    byte -> short -> int -> long -> float -> double
    char -> int -> long -> float -> double

最终整个条件表达式获得其第二个和第三个操作数的类型。

示例:
如果将charshort组合,则表达式变为int
如果将IntegerInteger组合,则表达式变为Integer
如果将final int i = 5Character组合,则表达式变为char
如果将shortfloat组合,则表达式变为float

在问题的示例中,200从Integer转换为double,0.0从Double取消装箱为double,整个条件表达式变成double,最终包装成Double,因为objObject类型。


本答案的目的是通过几个简单的规则表达完整的定义,包括拆箱、数值提升等,这些规则易于记忆。 - Gerhard

0

例子:

public static void main(String[] args) {
    int i = 10;
    int i2 = 10;
    long l = 100;
    byte b = 10;
    char c = 'A';
    Long result;
    // combine int with int result is int compiler error
   // result = true ? i : i2; // combine int with int, the expression becomes int
    //result = true ? b : c; // combine byte with char, the expression becomes int

    //combine int with long result will be long
    result = true ? l : i; // success - > combine long with int, the expression becomes long
    result = true ? i : l; // success - > combine int with long, the expression becomes long
    result = true ? b : l; // success - >  combine byte with long, the expression becomes long
    result = true ? c : l; // success - >  char long with long, the expression becomes long

    Integer intResult;
    intResult = true ? b : c; // combine char with byte, the expression becomes int.
   // intResult = true ? l : c; // fail combine long with char, the expression becomes long.

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接