为什么第二行在运行时抛出ClassCastException
?
Object obj = new Integer(2);
Byte b2 = (byte) obj; // at Runtime: ClassCastException "Integer cannot be cast to Byte"
我认为Integer(引用obj指向的)被取消装入int,然后转换为byte,然后装入Byte并成功分配。 Eclipse编译器警告也说(稍微纠正一下):
- Object类型的表达式取消装入字节
- 类型字节的表达式被装入字节
那么为什么它不能将RHS中的这个字节分配给LHS中的字节引用?
P.S。 Oracle javac编译器上的ClassCastException
相同
如果Object obj = new Integer(2);
,则Long b2 = (long) obj;
有效但Long b2 = 7;
失败。虽然Byte b2 = (byte) obj;
失败了,但Byte b2 = 7;
没问题!也许这些相互差异有一些线索?
根据经验,我会说在拆箱后禁止缩小原始类型(即使使用显式转换)(允许加宽) - 它可以解释这种行为。
终于明白了:
5.2。作业上下文(JLS): variable = expression
分配上下文允许使用以下之一:
身份转换(第5.1.1节)
扩大原始转换(第5.1.2节)
扩大参考转换(第5.1.5节)
拳击转换(§5.1.7),可选地后跟加宽 参考转换
取消装箱转化(第5.1.8节)可选地后跟a 扩展原始转换。
此外,如果表达式是常量表达式(§15.28) 类型为byte,short,char或int:
缩小原始转换,然后进行装箱转换 如果变量的类型为:
,则可以使用字节,常量表达式的值可以在字节类型中表示。
短,常量表达式的值可以在short类型中表示。
字符,常量表达式的值可在char类型中表示。
但这在运行时也很好:
Object o = new Integer(2);
Integer i2 = (Integer) o;
Integer i3 = (int) o;
我们需要进一步阐述“分配上下文”与“分配上下文” - 它们如何协同工作。
答案 0 :(得分:3)
Object obj = new Integer(2);
Byte b2 = (byte) obj;
装箱/取消装箱是静态,即在编译时确定。您已投放到Object
,因此编译器并不知道obj
实际上属于Integer
类型。相反,它生成假定Byte
实例的字节码,以及显式检查(在运行时失败):
ALOAD 1
CHECKCAST java/lang/Byte // Oh dear
INVOKEVIRTUAL java/lang/Byte.byteValue ()B // Unbox as a Byte
INVOKESTATIC java/lang/Byte.valueOf (B)Ljava/lang/Byte; // Box as a Byte
ASTORE 2
<小时/>
Integer
引用Integer obj = new Integer(2);
Byte b2 = (byte) obj;
第二行甚至无法编译。 (byte) obj
是投射上下文,
并且有bunch of rules定义了此处允许的内容。 1 不允许进行拆箱转换,然后进行缩小转换。
<小时/>
规则允许进行拆箱转换,然后进行扩展转换,因此此代码编译并运行时没有错误:
Integer obj = new Integer(2);
Long b2 = (long) obj;
让我们看看相应的字节码:
ALOAD 1
INVOKEVIRTUAL java/lang/Integer.intValue ()I // Unbox as an Integer
I2L // Convert to long
INVOKESTATIC java/lang/Long.valueOf (J)Ljava/lang/Long; // Box as a Long
ASTORE 2
我们看到两个不同之处:
Integer
而不是Byte
。CHECKCAST
,因为编译器确切知道存在什么类型。所以它实际上更有效率!
<小时/>
那么,你别无选择,只能执行相关的演员阵容:
Object obj = new Integer(2);
Byte b2 = (byte)(int)(Integer) obj;
为了完整起见,这里是相应的字节码:
ALOAD 1
CHECKCAST java/lang/Integer // This is now ok
INVOKEVIRTUAL java/lang/Integer.intValue ()I // Unbox as an Integer
I2B // Convert to byte
INVOKESTATIC java/lang/Byte.valueOf (B)Ljava/lang/Byte; // Box as a Byte
ASTORE 2
<子> 1。请注意,由于=
has its own set of rules,此处还有分配上下文。除此之外,人们不能Long b2 = 7;
,因为没有任何东西允许扩大转换,然后进行拳击转换。
答案 1 :(得分:1)
Casts在Java中不起作用。如你所见,两个包装类之间的转换会失败 - 它不会通过拆箱,扩展和重新装箱。如果你想实现这种行为,你必须自己完成这些步骤:
Object obj = new Integer(2);
Byte b2 = (byte) ((Integer)obj).intValue();