Java 强制转换运算符是如何工作的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/840322/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 14:00:28  来源:igfitidea点击:

How does the Java cast operator work?

javacastingjvm

提问by James McMahon

I am trying to debug an issue involving a ClassCastException in Java. In the interest of solving the issue I need to know what is going on when I cast from Object to a specific type. Can anyone explain to me how the Java cast operator works at the Java level and the JVM level?

我正在尝试调试一个涉及 Java 中 ClassCastException 的问题。为了解决这个问题,我需要知道当我从 Object 转换为特定类型时发生了什么。谁能向我解释 Java 转换运算符在 Java 级别和 JVM 级别是如何工作的?

采纳答案by Michael Myers

Is the JLSgood enough?

JLS不够好?

Casting conversion is applied to the operand of a cast operator (§15.16): the type of the operand expression must be converted to the type explicitly named by the cast operator. Casting contexts allow the use of:

  • an identity conversion (§5.1.1)
  • a widening primitive conversion (§5.1.2)
  • a narrowing primitive conversion (§5.1.3)
  • a widening reference conversion (§5.1.5) optionally followed by an unchecked conversion (§5.1.9)
  • a narrowing reference conversion (§5.1.6) optionally followed by an unchecked conversion
  • a boxing conversion (§5.1.7)
  • an unboxing conversion (§5.1.8).

强制转换应用于强制转换运算符的操作数(第 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 节)。

Actually, maybe this partis more relevant:

实际上,也许这部分更相关:

The detailed rules for compile-time legality of a casting conversion of a value of compile-time reference type Sto a compile-time reference type Tare as follows:

  • If Sis a class type:
    • If Tis a class type, then either |S| <: |T|, or |T| <: |S|; otherwise a compile-time error occurs. Furthermore, if there exists a supertype Xof T, and a supertype Yof S, such that both Xand Yare provably distinct parameterized types (§4.5), and that the erasures of Xand Yare the same, a compile-time error occurs.
    • If Tis an interface type:

      • If Sis not a finalclass (§8.1.1), then, if there exists a supertype Xof T, and a supertype Yof S, such that both Xand Yare provably distinct parameterized types, and that the erasures of Xand Yare the same, a compile-time error occurs. Otherwise, the cast is always legal at compile time (because even if Sdoes not implement T, a subclass of Smight).
      • If Sis a finalclass (§8.1.1), then Smust implement T, or a compile-time error occurs.

    • If Tis a type variable, then this algorithm is applied recursively, using the upper bound of Tin place of T.
    • If Tis an array type, then Smust be the class Object, or a compile-time error occurs.
  • If Sis an interface type:
    • If Tis an array type, then Tmust implement S, or a compile-time error occurs.
    • If Tis a type that is not final(§8.1.1), then if there exists a supertype Xof T, and a supertype Yof S, such that both Xand Yare provably distinct parameterized types, and that the erasures of Xand Yare the same, a compile-time error occurs. Otherwise, the cast is always legal at compile time (because even if Tdoes not implement S, a subclass of Tmight).
    • If Tis a type that is final, then:
      • If Sis not a parameterized type or a raw type, then Tmust implement S, and the cast is statically known to be correct, or a compile-time error occurs.
      • Otherwise, Sis either a parameterized type that is an invocation of some generic type declaration G, or a raw type corresponding to a generic type declaration G. Then there must exist a supertype Xof T, such that Xis an invocation of G, or a compile-time error occurs. Furthermore, if Sand Xare provably distinct parameterized types then a compile-time error occurs.
  • If Sis a type variable, then this algorithm is applied recursively, using the upper bound of Sin place of S.
  • If Sis an array type SC[], that is, an array of components of type SC:
    • If Tis a class type, then if Tis not Object, then a compile-time error occurs (because Objectis the only class type to which arrays can be assigned).
    • If Tis an interface type, then a compile-time error occurs unless Tis the type java.io.Serializableor the type Cloneable, the only interfaces implemented by arrays.
    • If Tis a type variable, then:
      • If the upper bound of Tis Objector the type java.io.Serializableor the type Cloneable, or a type variable that Scould legally be cast to by recursively applying these rules, then the cast is legal (though unchecked).
      • If the upper bound of Tis an array type TC[], then a compile-time error occurs unless the type SC[]can be cast to TC[] by a recursive application of these compile-time rules for casting.
      • Otherwise, a compile-time error occurs.
    • If Tis an array type TC[], that is, an array of components of type TC, then a compile-time error occurs unless one of the following is true:

    • TCand SCare the same primitive type.
    • TCand SCare reference types and type SCcan be cast to TCby a recursive application of these compile-time rules for casting.

将编译时引用类型S的值强制转换为编译时引用类型T 的编译时合法性的详细规则如下:

  • 如果 S是类类型:
    • 如果T是一个类类型,那么要么 | 小号| <: | T|,或 | T| <: | 小号|; 否则会发生编译时错误。此外,如果存在的超类型XŤ,和超类型ÿ小号,使得两个Xÿ是可证明不同参数化类型(§4.5),并且的擦除Xÿ是相同的,编译时发生错误。
    • 如果T是接口类型:

      • 如果 小号不是final类(§8.1.1),然后,如果存在的超类型 XŤ,和超类型 ÿ小号,使得两个 Xÿ是可证明不同参数化类型,那的擦除XY相同,则发生编译时错误。否则,转换在编译时总是合法的(因为即使 S没有实现TS的子类也可能)。
      • 如果S是一个 final类(第 8.1.1 节),则S必须实现T,否则会发生编译时错误。

    • 如果T是类型变量,则递归应用此算法,使用T的上限代替T
    • 如果T是数组类型,则S必须是 class Object,否则会发生编译时错误。
  • 如果S是接口类型:
    • 如果T是数组类型,则T必须实现S,否则会发生编译时错误。
    • 如果Ť是一个类型不 final(§8.1.1),然后,如果存在的超类型 XŤ,和超类型 ÿ小号,使得两个 Xÿ是可证明不同参数化类型,那的擦除XY相同,则发生编译时错误。否则,转换在编译时总是合法的(因为即使 T没有实现ST的子类也可能)。
    • 如果T是 的类型final,则:
      • 如果S不是参数化类型或原始类型,则T必须实现S,并且强制转换在静态上是正确的,否则会发生编译时错误。
      • 否则, S要么是调用某个泛型类型声明G的参数化类型,要么是对应于泛型类型声明G的原始类型。然后必须存在一个超类型XŤ,使得X是的调用ģ,或发生编译时间错误。此外,如果SX是可证明不同的参数化类型,则会发生编译时错误。
  • 如果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的组件的数组,则除非满足以下任一条件,否则会发生编译时错误:

    • TCSC是相同的原始类型。
    • TCSC是引用类型,并且类型 SC可以通过这些编译时规则的递归应用转换为TC进行转换。

Perfectly clear now, isn't it? :D

现在完全清楚了,不是吗?:D

In other words, this is the best I can do without knowing more details about your problem.

换句话说,这是我在不了解有关您的问题的更多详细信息的情况下所能做的最好的事情。

回答by djna

A likely cause of class cast mystifcation is that not only do the types have to match but also they must be loaded by the same classloader.

类转换神秘化的一个可能原因是,不仅类型必须匹配,而且它们必须由同一个类加载器加载。

You should be able to dump not only the type hierarchy but also the identity of the classloader for each class.

您不仅应该能够转储类型层次结构,还应该能够转储每个类的类加载器的标识。

These kind of problems are not uncommon in appserver-style environments where application code and infratructure code are deliberately isolated - for example if system classes are accidentally included in applciation JARs you can have two copies of the "same" class inthe JVM and life gets confusing

在应用程序代码和基础结构代码被故意隔离的应用程序服务器风格的环境中,这类问题并不少见——例如,如果系统类意外地包含在应用程序 JAR 中,您可能会在 JVM 中拥有“相同”类的两个副本,并且生活会变得混乱

回答by erickson

Other useful and authoritative references are found in the Java Virtual Machine Specification, specifically §2.6.5, "Narrowing Reference Conversions",and especially the definition of the checkcastinstruction.

在 Java 虚拟机规范中可以找到其他有用和权威的参考,特别是第2.6.5 节“缩小引用转换”,尤其是checkcast指令的定义。

回答by erickson

Casting asserts that the runtime type of an object is compatible with the given static type, and thus allows you to call methods of that type on the object.

强制转换断言对象的运行时类型与给定的静态类型兼容,因此允许您在对象上调用该类型的方法。

Here obj is a Integer object, but only accessible though an Object reference:

这里 obj 是一个 Integer 对象,但只能通过 Object 引用访问:

Object obj = new Integer(1);

Casting lets you treat it as an Integer (or some superclass of Integer) again:

转换让您再次将其视为 Integer(或 Integer 的某些超类):

System.out.println(((Integer) obj).intValue());

ClassCastException occours when the static type given does not match the runtime type of the object:

当给定的静态类型与对象的运行时类型不匹配时,会发生 ClassCastException:

System.out.println(((Float) obj).intValue()); // runtime error

You can find the runtime type of any object by using getClass() and the various Class methods:

您可以使用 getClass() 和各种 Class 方法找到任何对象的运行时类型:

System.out.println(obj.getClass()); // prints "class java.lang.Integer"