Java强制转换操作符如何工作?
我正在尝试调试Java中涉及ClassCastException的问题。 为了解决这个问题,我需要知道当我从Object转换为特定类型时会发生什么。 任何人都可以向我解释Java强制转换操作符在Java级别和JVM级别上的工作原理吗?
JLS足够好吗?
转换转换应用于转换运算符的操作数(第15.16节):操作数表达式的类型必须转换为转换运算符显式命名的类型。 转换上下文允许使用:
- 身份转换(第5.1.1节)
- 扩展的原始转换(第5.1.2节)
- 缩小的原始转换(第5.1.3节)
- 扩展引用转换(第5.1.5节),可选地后跟未经检查的转换(第5.1.9节)
- 缩小的引用转换(第5.1.6节),可选地后跟未经检查的转换
- 拳击转换(§5.1.7)
- 拆箱转换(第5.1.8节)。
实际上,也许这部分更相关:
将编译时引用类型S的值转换为编译时引用类型T的编译时合法性的详细规则如下:
- 如果S是类类型:
- 如果T是类类型,则为| S | <:| T |,或| T | <:| S |; 否则会发生编译时错误。 此外,如果存在T的超类型X和S的超类型Y ,使得X和Y都可certificate是不同的参数化类型(第4.5节),并且X和Y的擦除是相同的,则编译时发生错误。
- 如果T是接口类型:
- 如果S不是
final
类(第8.1.1节),那么,如果存在T的超类型X和S的超类型Y ,那么X和Y都可certificate是不同的参数化类型,并且X的擦除和Y是相同的,发生编译时错误。 否则,强制转换在编译时总是合法的(因为即使S没有实现T , S的子类也可能)。- 如果S是
final
类(第8.1.1节),那么S必须实现T ,否则会发生编译时错误。如果T是一个类型变量,则递归地应用该算法,使用T的上限代替T. 如果T是数组类型,则S必须是 Object
类,否则会发生编译时错误。如果S是接口类型:
- 如果T是数组类型,则T必须实现S ,否则会发生编译时错误。
- 如果T是一个非
final
的类型(§8.1.1),那么如果存在T的超类型X和S的超类型Y ,那么X和Y都可以certificate是不同的参数化类型,并且X和Y相同,发生编译时错误。 否则,强制转换在编译时总是合法的(因为即使T没有实现S , T的子类也可能)。- 如果T是
final
的类型,那么:
- 如果S不是参数化类型或原始类型,则T必须实现S ,并且静态地知道强制转换是正确的,否则会发生编译时错误。
- 否则, S是参数化类型,其是一些generics类型声明G的调用,或者是对应于generics类型声明G的原始类型。 然后必须存在T的超类型X ,这样X是G的调用,或者发生编译时错误。 此外,如果S和X可certificate是不同的参数化类型,则发生编译时错误。
如果S是类型变量,则递归地应用该算法,使用S的上限代替S。 如果S是数组类型SC [],即SC类型的组件数组:
- 如果T是类类型,那么如果T不是
Object
,则会发生编译时错误(因为Object
是唯一可以为其分配数组的类类型)。- 如果T是接口类型,则发生编译时错误,除非T是
java.io.Serializable
类型或Cloneable
类型,这是由数组实现的唯一接口。- 如果T是一个类型变量,那么:
- 如果T的上限是
Object
或java.io.Serializable
类型或Cloneable
类型,或者通过递归应用这些规则可以合法地转换为S的类型变量,则转换是合法的(虽然未选中)。- 如果T的上限是数组类型TC [] ,则发生编译时错误,除非通过递归应用这些编译时规则进行转换,可以将类型SC []强制转换为TC [] 。
- 否则,发生编译时错误。
- 如果T是数组类型TC [],即TC类型的组件数组,则会发生编译时错误,除非满足下列条件之一:
TC和SC是相同的原始类型。 TC和SC是引用类型,类型SC可以通过递归应用这些编译时规则进行转换来转换为TC 。
现在完全清楚,不是吗? :d
换句话说,如果不了解有关您问题的更多详细信息,这是我能做的最好的事情。
类转换神秘化的一个可能原因是,不仅类型必须匹配,而且它们必须由相同的类加载器加载。
您不仅可以转储类型层次结构,还可以转储每个类的类加载器的标识。
这些问题在应用程序风格的环境中并不罕见,其中应用程序代码和基础结构代码是故意隔离的 – 例如,如果系统类意外地包含在应用程序JAR中,您可以在JVM中拥有两个“相同”类的副本,并且生活变得混乱
Casting断言对象的运行时类型与给定的静态类型兼容,因此允许您在对象上调用该类型的方法。
这里obj是一个Integer对象,但只能通过Object引用访问:
Object obj = new Integer(1);
Casting允许您再次将其视为Integer(或Integer的某个超类):
System.out.println(((Integer) obj).intValue());
当给定的静态类型与对象的运行时类型不匹配时,ClassCastException会出现:
System.out.println(((Float) obj).intValue()); // runtime error
您可以使用getClass()和各种Class方法找到任何对象的运行时类型:
System.out.println(obj.getClass()); // prints "class java.lang.Integer"
其他有用和权威的参考资料可以在Java虚拟机规范中找到,特别是§2.6.5,“缩小参考转换”,特别是checkcast
指令的定义。