8

以下程序分别打印“false”和“true”:

Number n = true ? new Long(1) : new Double(2.0);
System.out.println(n instanceof Long);
System.out.println(n instanceof Double);

所以它不会是 Long 而是 Double。但是,它在普通类上按预期工作:

class B {}
class D1 extends B {}
class D2 extends B {}

这将打印“真”:

B b = true ? new D1() : new D2();
System.out.println(b instanceof D1);

这意味着它与上面的示例不同。

我确信有一些与自动装箱有关的东西,但这真的是它应该工作的方式吗?当 Number 类是 Long 和 Double 的超类时,为什么她使用拳击,以便表达式可以计算为 Number?

这真的很痛苦,因为在打印 n 时,它会打印为 double 值。(我知道这很容易解决,但让我发疯了)

4

2 回答 2

7

让我们在这里拿出语言律师的书:JLS §15.25

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

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

Long 和 Double 不是同一类型 - 不适用。

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

这两个值都不是原始值 - 不适用。

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

两个值都不是 null - 不适用。

  • 否则,如果第二个和第三个操作数具有可转换(第 5.1.8 节)为数字类型的类型,则有几种情况:
    • [...字节/短/字符及其盒装等价物的特殊情况...]
    • 否则,二进制数值提升(第5.6.2节)将应用于操作数类型,条件表达式的类型是第二个和第三个操作数的提升类型。

此规则在这里适用,这意味着条件运算符的结果类型就像两个值都未装箱一样。假设其背后的原因是否则Number n = bool ? 1 : 2.0并且Number n = bool ? new Long(1) : new Double(2.0)具有不同的值。这种行为也会出乎意料,而且——更糟糕的是——不一致。

于 2012-10-08T20:46:07.357 回答
2

它只是查看字节码,您会看到(只需修改您的示例)

Number n = true ? new Long(166666) : new Double(24444.0);
System.out.println(Boolean.toString(n instanceof Long));
System.out.println(Boolean.toString(n instanceof Double));

字节码

_new 'java/lang/Long'

dup
ldc 166666
invokespecial 'java/lang/Long.<init>','(J)V'
invokevirtual 'java/lang/Long.longValue','()J'
l2d
invokestatic 'java/lang/Double.valueOf','(D)Ljava/lang/Double;'
astore 1

重点是l2d它使下一步

从堆栈中弹出一个长整数,将其转换为双精度浮点数,然后将双精度数推回堆栈。请注意,这可能会导致精度损失(double 的有效位是 54 位,而 long 是 64 位),但不会损失幅度(因为 double 的范围大于 long 的范围)。使用 IEEE 754 舍入到最近模式进行舍入。

在这一切都很好之后,你将拥有Double实例,但具有Long Value!如果您在 Debug 模式下查看,您会看到我们的数字是 Double 但值来自 Long,它在上面的字节码中描述

我们可以在字节码中看到它

getstatic 'java/lang/System.out','Ljava/io/PrintStream;'
aload 1
_instanceof 'java/lang/Long'
invokestatic 'java/lang/Boolean.toString','(Z)Ljava/lang/String;'
invokevirtual 'java/io/PrintStream.println','(Ljava/lang/String;)V'
getstatic 'java/lang/System.out','Ljava/io/PrintStream;'
aload 1
_instanceof 'java/lang/Double'
invokestatic 'java/lang/Boolean.toString','(Z)Ljava/lang/String;'
invokevirtual 'java/io/PrintStream.println','(Ljava/lang/String;)V'
return
于 2012-10-08T20:50:08.573 回答