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

Java - ternary operator weird behaviour

我试图从 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

这是怎么回事?

选择正确。然后它把它包裹成双层。这是 3 个关键点:

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

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

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

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

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

打印运行时类型为 IntegerObject 得到 5 而打印 Double 得到 5.0.

表达式 a ? b : c 的类型始终与 c 相同或 bc 的最近公共父级相同。

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

顺便说一句 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));

在你的例子中,三元运算符的第二个和第三个参数是类型 "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);