Java 泛型 class 转换异常

Java generics class cast exception

我正在尝试创建一个 class 来处理可比对象。我归结为最简单的代码,当我尝试实例化 class 时会出错。我收到了几个编译警告(未经检查的转换),但是当我 运行 这个程序时它抛出一个 class 转换异常。我确实查看了有关该主题的其他一些问题,但没有发现有用的东西。

public class GD<Item extends Comparable<Item>> {
   private Item[] data;
   private final int MAX_SIZE = 200;
   public GD() {
      data = (Item[]) new Object[MAX_SIZE];
   }

   public static void main(String[] args) {
     GD<String> g = new GD<String>();
   }
}

问题出在这里:

data = (Item[]) new Object[MAX_SIZE];

您正在实例化 Object 的数组,然后尝试将其转换为 Item 的数组,这会引发异常,因为 Object 不会扩展您的 [=14] =] class,因为它没有实现Comparable。您想要的是:

data = new Item[MAX_SIZE];

但是你不能这样做,因为 Item 是一个通用类型。如果你想动态创建这种类型的对象(或对象数组),你需要将 Class 对象传递给你的 GD class 的构造函数:

import java.lang.reflect.Array;

public class GD<Item extends Comparable<Item>> {
   private Item[] data;
   private final int MAX_SIZE = 200;
   public GD(Class<Item> clazz) {
      data = (Item[]) Array.newInstance(clazz, MAX_SIZE);
   }

   public static void main(String[] args) {
     GD<String> g = new GD<String>(String.class);
   }
}

很简单:Object 没有实现 Comparable<Object>,但是你强制执行 Item extends Comparable<Item>。因此,您不能将 Object[] 转换为 Item[].

为了避免创建泛型数组Item的问题,我们可以看看Java的ArrayList implementation. Here, the backing array is of type Object and only accessible via mutator methods:

private Object[] data; // Make the backing array private, access only via mutator methods

public void add(Item i)
{
    int idx = ... // calculate next free index somehow
    this.data[idx] = i;
}

public Index get(int idx) {
    return ((Item) (this.data[idx]));
}

因为你只允许插入 Items,所以你只能插入 return Items,因此你永远不会有 ClassCastException.

关于您的代码的一些评论:通常,使用 TUS 作为类型参数。如果你使用像 Item 这样的东西,人们可能会将类型参数与实际的 class.

混淆

强制转换 (Item[]) 不会根据 Item[] 检查对象的运行时类型,因为在运行时不知道 Item 是什么;相反,它会检查 Item[] 的擦除,即 Comparable[](因为 ComparableItem 的擦除)。实际运行时间 class 为 Object[] 的对象不是 Comparable[] 的实例,因此它会导致 ClassCastException。这就是为什么简单地做

data = (Item[]) new Comparable[MAX_SIZE];

将使异常消失。

所以有两种方法可以解决这个问题:

  • 理论上的类型"safe"方式,即使用固定的非特定数组类型,如Object[]Comparable[]作为编译时类型data 变量。这样您就可以创建一个相同运行时类型的数组对象,并将其安全地分配给变量,而无需进行不安全的强制转换。但是,当您需要从数组中取出某些内容并将其 return 作为泛型类型时,您将不得不进行显式(未经检查)转换:

    public class GD<Item extends Comparable<Item>> {
       private Object[] data;
       private final int MAX_SIZE = 200;
       public GD() {
          data = new Object[MAX_SIZE];
       }
       public Item get(int index) {
          return (Item)data.get(index);
       }
    }
    
  • 或者,您可以将 data 保留为 Item[],方法是在创建它时从 new Comparable[] 进行不安全转换。请注意,这在理论上是无效的转换,因为数组的实际运行时间 class 是 Comparable[],通常不是 Item[] 的实例。但是,在这个class里面,Item被抹成了Comparable,这个谎言不会造成任何问题,只要声称dataItem[]没有暴露在 class 的外部(例如,如果 data 是 public,或者如果有像 getArray() 这样的方法 returned data直接写成Item[],那就不好了)。这种方式的好处是当你从数组中取出一些东西时,你不必进行强制转换。缺点是程序员要小心,不要把这个关于类型的谎言暴露给外界。

    public class GD<Item extends Comparable<Item>> {
       private Item[] data;
       private final int MAX_SIZE = 200;
       public GD() {
          data = (Item[])new Comparable[MAX_SIZE];
       }
       public Item get(int index) {
          return data.get(index);
       }
    }