【问题标题】:Why can't I cast an array of Objects to an array of a generic subclass of a generic class?为什么我不能将对象数组转换为泛型类的泛型子类数组?
【发布时间】:2020-12-09 07:57:07
【问题描述】:

此代码有效。它编译并运行时会出现“未经检查或不安全的操作”警告。

class Foo<T> {
  T[] items = (T[]) new Object[10];

  public static void main(String[] args) {
    Foo<Integer> foo = new Foo<Integer>();
  }
}

虽然这两个给了我运行时错误

class Foo<T> {
  class FooItem { T item; }

  FooItem[] items = (FooItem[]) new Object[10];

  public static void main(String[] args) {
    Foo<Integer> foo = new Foo<Integer>();
  }
}
class Foo<T> {
  static class FooItem<E> { E item; }

  FooItem<T>[] items = (FooItem<T>[]) new Object[10];

  public static void main(String[] args) {
    Foo<Integer> foo = new Foo<Integer>();
  }
}

我遇到的错误如下:

线程“main”中的异常 java.lang.ClassCastException: class [Ljava.lang.Object;不能转换为类 [LFoo$FooItem; ([Ljava.lang.Object; 位于加载器'bootstrap' 的模块 java.base 中;[LFoo$FooItem; 位于加载器 com.sun.tools.javac.launcher.Main$MemoryClassLoader 的未命名模块中

这是为什么?

【问题讨论】:

  • "these 2 don't compile" 具有误导性。您得到的是运行时异常,而不是编译器错误。
  • 你是对的,它们实际上是编译的!我已编辑问题以将其声明为运行时错误。
  • Java 和 C# 中的数组差异刚刚被破坏。 web.archive.org/web/20100629084706/http://blogs.msdn.com/b/…

标签: java


【解决方案1】:

Foo 类的所有版本都有相同的问题。您忽略的 Type safety: Unchecked cast from Object[] to X 警告只是显示了执行期间不同阶段的后果。

这是破坏您的第一个版本的一种方法:

class Foo<T> {
    T[] items = (T[]) new Object[10];

    public static void main(String[] args) {
        Foo<Integer> foo = new Foo<Integer>();

        Integer i = foo.items[1] * 2;
    }
}

它崩溃了......同样的问题:

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
    at stackoverflow.Foo.main(Main.java:17)

为什么?因为(T[]) new Object[10];在编译时没有强制执行,即编译器不知道如何验证T[]Object[]之间的关系

正如您不能将new Object() 转换为Integer 一样,您也不能将new Object[10] 转换为Integer[]。只是这个类转换异常只在实际转换运行时出现。对于第一个 sn-p,它不会在声明语句 T[] items = (T[]) new Object[10];(因此出现警告)上运行,但当 items 用作 T 的任何类型参数时。

您的其他版本会更快地显示问题,因为它们正在转换为具体类型,例如 (FooItem[]) new Object[10],导致声明语句本身崩溃。
为什么编译器允许这种转换?因为类型关系是有效的,给定这样的代码

Object[] items = new Integer[10];
Integer[] i = (Integer[])items;
Integer val = i[0];

有效且受支持。

【讨论】:

    【解决方案2】:

    您不能将Object[] 转换为T[] 的原因是这可能会导致以下代码出现问题:

    Object[] objects = new Object[10];         // This is fine
    objects[0] = "Hello World";                // Is fine since String is subtype of Object
    T[] ts = (T[]) objects;                    // One could argue that this is fine
    T firstT = ts[0];                          // Now we have the issue that String needs to be casted to T
    

    因为这种将Object[] 转换为T[] 是不允许的。列表等泛型也是如此。

    【讨论】:

    • T[] ts = (T[]) new Object[n]; 实际上是允许使用 Java 警告的。我认为强制转换仅适用于泛型类型。
    • 是的,这是允许的,但它给出了一个警告,正是出于上述原因。但我同意,“不能”的表述可能有点误导。泛型在那里有点不同,因为这是一个仅在编译期间存在的构造。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-19
    相关资源
    最近更新 更多