【问题标题】:Casting arrays of a supertype to a subtype将超类型的数组转换为子类型
【发布时间】:2013-04-12 02:09:35
【问题描述】:

这是否会导致运行时异常而不是 Java 中的编译时错误?

Object[] objects = new Object[10];
String[] strings = (String[])objects;

【问题讨论】:

    标签: java arrays casting


    【解决方案1】:

    由于这种情况,必须在运行时进行检查:

    public class Test {
      public static void main(String[] args){
        String[] stringsBase = {"aaa", "bbb", "ccc"};
        Object[] objects = stringsBase;
        String[] strings = (String[])objects;
        System.out.println(strings[1]);
      }
    }
    

    这是一个有效的工作程序。如果不进行流分析,编译器不知道对象是否引用了作为 Object[] 创建的数组,或者在本例中作为 String[] 创建的数组。

    【讨论】:

    • 为什么编译器不处理这些情况?
    • 嗯?编译器处理得很好。
    • 澄清一下,我的意思是:为什么编译器不够聪明,无法区分转换String[] → Object[] → String[]String[] → Object[] → Integer[]?这是一个引导性的问题。答案当然是the JLS does not require the compiler to perform such checks.
    • JLS 这么说将问题推后一级。它仍然留下了为什么 JLS 这么说的问题,这就是我试图回答的问题。总的来说,我们是否正在处理有效的演员阵容是不可判定的。我想 JLS 可能需要类似于“明确分配”系统的东西,但是如果没有相反的有力证据,可以更简单、更清晰地假设程序员编写演员表是故意的,应该留下测试运行时间。
    • @MattBall - JLS 不需要实现来做到这一点,因为在许多情况下这是不可能的(例如,如果在第一次转换之后,该值用作 a 的参数反转演员表的方法)。
    【解决方案2】:

    因为 这是语言规范定义的编译时行为。 简短的版本是 Object[] 可以转换为 String[] 而不会产生编译时错误,因为Object 可以转换为 String 而不会产生编译时错误。


    长答案只是我引用了 JLS。来自the Java Language Specification § 5.5.1. Reference Type Casting:

    给定一个编译时引用类型 S(源)和一个编译时引用类型 T(目标),如果由于以下规则而没有发生编译时错误,则存在从 S 到 T 的强制转换。
    ...
    如果S是数组类型SC[],即SC类型的组件数组:
    ...

    • 如果 T 是数组类型 TC[],即 TC 类型的组件数组,则除非满足以下条件之一,否则会发生编译时错误:
      • TC 和 SC 是相同的原始类型。
      • TC 和 SC 是引用类型,类型 SC 可以转换为 TC。

    根据同一节中关于转换的前面的规则,Objects 可以转换为 Strings:

    如果 S 是类类型:

    • 如果 T 是类类型,那么 |S| <:>

    以防万一您想知道:

    根据§5.5.1. 中的规则,如果没有发生编译时错误,则引用类型的表达式可能会转换为另一个引用类型


    注意the JLS uses |T| to denote the erasure of type TS :> T indicates that the supertype relation holds between S and T. 因此“|S| <: or s t>

    【讨论】:

    • 只是出于好奇......我可以编译这个事实怎么样:StringBuilder sb = new StringBuilder(); Object o = sb; Integer i = (Integer) o; 你能解释一下为什么吗?这似乎表明上述情况不是一个特殊情况......但我确定我误解了一些东西。
    • @jahroy 的答案已经过大量编辑,但第二个引用涵盖了这一点。
    • 嗯.. 希望我知道那个符号。但我真的不明白这将如何应用......我不想在我的小例子中转换为 String 。我正在尝试将 StringBuilder 转换为 Integer (在运行时不起作用,但可以编译)。如果您不想回答,请不要担心......只是好奇。
    • @jahroy 只是添加了对符号的解释,因为我自己并没有立即理解它。 JLS 只是不要求编译器那么聪明。中间转换为Object 在编译器的混乱中。如果您真的想了解,请花时间深入了解 JLS §5.5.1。和 5.5.2.
    • 这回答了问题。它实际上可以应用于关于 Java 行为的任何问题:“为什么当我尝试取消引用包含 null 的变量时,Java 会抛出 NullPointerException?因为 JLS 说它必须这样做。”归根结底,关于 Java 为什么会执行 JLS 指定的事情的问题应该被解释为为什么 JLS 是以这种特定方式编写的问题,否则它是没有意义的。
    【解决方案3】:

    强制转换是 Java 出于灵活性目的而提供的一个根本不安全的特性。如果您的编译器设置正确调整,该代码会生成“未经检查”的警告。

    【讨论】:

    • 嗯。这是一个相当空洞的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-29
    • 1970-01-01
    • 2012-05-25
    • 1970-01-01
    • 2013-05-01
    • 2015-03-12
    • 1970-01-01
    相关资源
    最近更新 更多