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的超类型XS的超类型Y ,使得XY都可certificate是不同的参数化类型(第4.5节),并且XY的擦除是相同的,则编译时发生错误。
    • 如果T是接口类型:
  • 如果S不是final类(第8.1.1节),那么,如果存在T的超类型XS的超类型Y ,那么XY都可certificate是不同的参数化类型,并且X的擦除和Y是相同的,发生编译时错误。 否则,强制转换在编译时总是合法的(因为即使S没有实现TS的子类也可能)。
  • 如果Sfinal类(第8.1.1节),那么S必须实现T ,否则会发生编译时错误。
  • 如果T是一个类型变量,则递归地应用该算法,使用T的上限代替T.
  • 如果T是数组类型,则S必须是Object类,否则会发生编译时错误。
  • 如果S是接口类型:
    • 如果T是数组类型,则T必须实现S ,否则会发生编译时错误。
    • 如果T是一个非final的类型(§8.1.1),那么如果存在T的超类型XS的超类型Y ,那么XY都可以certificate是不同的参数化类型,并且XY相同,发生编译时错误。 否则,强制转换在编译时总是合法的(因为即使T没有实现ST的子类也可能)。
    • 如果Tfinal的类型,那么:
      • 如果S不是参数化类型或原始类型,则T必须实现S ,并且静态地知道强制转换是正确的,否则会发生编译时错误。
      • 否则, S是参数化类型,其是一些generics类型声明G的调用,或者是对应于generics类型声明G的原始类型。 然后必须存在T的超类型X ,这样XG的调用,或者发生编译时错误。 此外,如果SX可certificate是不同的参数化类型,则发生编译时错误。
  • 如果S是类型变量,则递归地应用该算法,使用S的上限代替S。
  • 如果S是数组类型SC [],即SC类型的组件数组:
    • 如果T是类类型,那么如果T不是Object ,则会发生编译时错误(因为Object是唯一可以为其分配数组的类类型)。
    • 如果T是接口类型,则发生编译时错误,除非Tjava.io.Serializable类型或Cloneable类型,这是由数组实现的唯一接口。
    • 如果T是一个类型变量,那么:
      • 如果T的上限是Objectjava.io.Serializable类型或Cloneable类型,或者通过递归应用这些规则可以合法地转换为S的类型变量,则转换是合法的(虽然未选中)。
      • 如果T的上限是数组类型TC [] ,则发生编译时错误,除非通过递归应用这些编译时规则进行转换,可以将类型SC []强制转换为TC []
      • 否则,发生编译时错误。
    • 如果T是数组类型TC [],即TC类型的组件数组,则会发生编译时错误,除非满足下列条件之一:
  • TCSC是相同的原始类型。
  • TCSC是引用类型,类型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指令的定义。