Java – 三元运算符奇怪的行为

我试图从一个double中删除fractional部分,以防它完全使用:

 (d % 1) == 0 ? d.intValue() : d 

并遇到以下我不明白的行为:

 public static void main(String[] args) { Double d = 5D; System.out.println((d % 1) == 0); // true System.out.println((d % 1) == 0 ? d.intValue() : "not whole"); // 5 System.out.println((d % 1) == 0 ? d.intValue() : d); // 5.0 } 

正如您在第三行中看到的那样,即使满足条件(d % 1) == 0 ,运算符也会选择else值 – 5.0

这里发生了什么?

三元条件运算符的返回类型必须使第二个和第三个操作数都可以分配给它。

因此,在第二种情况下,运算符的返回类型是Object (因为d.intValue()"not whole"必须赋值给它)而在第三种情况下它是Double (因为d.intValue()d必须可分配给它)。

打印运行时类型为IntegerObject为您提供5而打印Double5.0

表达式的类型a ? b : c a ? b : c始终与cbc的最近共同父项相同。

 System.out.println((d % 1) == 0 ? d.intValue() : "not whole"); // Comparable a parent of Integer and String System.out.println((d % 1) == 0 ? d.intValue() : d); // Double is a widened int 

BTW d % 1只会检查它是否为整体,而不是它足够小以适应int值。 更安全的检查是在转换为intlong时查看值是否相同

 double d = 5.0; if ((long) d == d) System.out.println((long) d); else System.out.println(d); 

或者你可以防止它将long扩大到双倍

 double d = 5.0; System.out.println((long) d == d ? Long.toString((long) d) : Double.toString(d)); 

它选择正确。 然后它将它包装成双倍。 这些是3个要点:

  1. 如果第二个和第三个操作数具有相同的类型,那么这是条件表达式的类型。 换句话说,你可以通过避开混合型计算来避免整个混乱。

  2. 如果其中一个操作数是T类型,其中T是byte,short或char,另一个操作数是int类型的常量表达式,其值可以在类型T中表示,条件表达式的类型是T.

  3. 否则,二进制数字提升将应用于操作数类型,条件表达式的类型是第二个和第三个操作数的提升类型。

在您的情况下,ternery运算符的第二个和第三个参数是“int”和“Double”类型。 Java必须将这些值转换为相同的类型,以便可以从三元运算符返回它们。 执行此操作的规则在Java语言规范中给出。 https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25

在您的情况下,这些规则导致两个参数都转换为“double”类型(“Double”是未装箱的,int是值转换的)。

修复是将参数强制转换为三元运算符,使它们属于同一类型(下面可能有更多括号,而不是严格需要,我对java运算符优先级规则有点生疏)。

 System.out.println((d % 1) == 0 ? ((Number)(d.intValue())) : (Number)d);