以下程序分别打印'false'和'true':
Number n = true ? new Long(1) : new Double(2.0);
System.out.println(n instanceof Long);
System.out.println(n instanceof Double);
所以它不会是长而是双。但是,它适用于普通类: 具有
class B {}
class D1 extends B {}
class D2 extends B {}
这将打印'true':
B b = true ? new D1() : new D2();
System.out.println(b instanceof D1);
意味着它的工作方式与上例不同。
我确信有一些与自动装箱相关的东西,但它真的应该如何运作吗?为什么她使用装箱,当Number类是Long和Double的超类时,表达式可以被评估为Number?
这真的很痛苦,因为在打印n时,它会打印为double值。 (我知道解决方法很容易,但让我发疯了)
答案 0 :(得分:7)
让我们在这里取出语言律师的书:JLS §15.25
条件表达式的类型确定如下:
- 如果第二个和第三个操作数具有相同的类型(可能是null类型),那么这就是条件表达式的类型。
长和双不是同一类型 - 不适用。
- 如果第二个和第三个操作数之一是原始类型T,而另一个操作数是应用装箱转换的结果 (§5.1.7)到T,那么条件表达式的类型是T。
这两个值都不是原始的 - 不适用。
- 如果第二个和第三个操作数之一是null类型而另一个的类型是引用类型,那么 条件表达式是引用类型。
两个值都不为null - 不适用。
此规则在此处适用,这意味着条件运算符的结果类型就像两个值都是未装箱的一样。假设背后的原因是否则Number n = bool ? 1 : 2.0
和Number n = bool ? new Long(1) : new Double(2.0)
具有不同的值。这种行为也是出乎意料的 - 更糟糕的是 - 不一致。
答案 1 :(得分: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
它会做出后续步骤
从堆栈中弹出一个长整数,将其转换为双精度 浮点数,并将双推回堆栈。 请注意,这可能会导致精度损失(a中的有效数字) double是54位,相比之下64位(虽然没有丢失) 大小(因为双倍的范围大于。的范围) 很久)。使用IEEE 754舍入到最近模式完成舍入。
在此之后一切都很好,所以你将拥有 Double 实例,但长值!如果您在调试模式下查看,您将看到我们的数字是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