数字上不需要的自动装箱魔法

时间:2012-10-08 20:20:05

标签: java autoboxing

以下程序分别打印'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值。 (我知道解决方法很容易,但让我发疯了)

2 个答案:

答案 0 :(得分:7)

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

  

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

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

长和双不是同一类型 - 不适用。

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

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

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

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

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

此规则在此处适用,这意味着条件运算符的结果类型就像两个值都是未装箱的一样。假设背后的原因是否则Number n = bool ? 1 : 2.0Number 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