如何将泛型与类数组一起使用?

时间:2009-04-14 21:25:49

标签: java generics

我想创建一个Classes数组,每个Classes表示我正在构建的系统中可用的类型。涉及的所有类都是公共超类的子类。所以我想这样做:

Class<? extends SuperClass>[] availableTypes = { SubClass1.class, SubClass2.class };

这给了我错误:

Cannot create a generic array of Class<? extends SuperClass>.

如果我尝试在初始化的右侧限定数组的创建,我会得到相同的消息:

Class<? extends SuperClass>[] availableTypes = Class<? extends SuperClass>[] { SubClass1.class, SubClass2.class };

如果我取消了泛型资格,我可以获得编译代码:

Class[] availableTypes = { SubClass1.class, SubClass2.class };

但后来我得到了泛型警告:

Class是原始类型。应该参数化泛型类的引用。

我正在努力;我尝试着! :)此外,在这一点上,即使这没有引起警告,我也失去了我试图定义的界面。我不想只返回一个任意类的数组;我想返回一个类的数组,这些类都是特定SuperClass的子类!

Eclipse有一些非常强大的工具可用于确定用于修复泛型声明的参数,但在这种情况下,它会降低,因为它在处理Class时往往会失败。它提供的“推断通用类型参数”程序根本不会更改代码,而是留下警告。

我可以通过使用Collection来解决这个问题:

List<Class<? extends SuperClass>> availableTypes = new List<Class<? extends SuperClass>>();

但是用数组做这个的正确方法是什么?

6 个答案:

答案 0 :(得分:23)

这似乎有点失败,但这样的问题正是大多数人避免混合数组和泛型的原因。由于泛型的实现方式(type erasure),数组和泛型将永远无法很好地协同工作。

两个解决方法:

  • 坚持使用一个集合(例如ArrayList<Class<? extends SuperClass>>),它与数组一样有效,也允许扩展。
  • 在创建数组的代码上添加@SuppressWarnings("unchecked")注释以及证明其使用的注释。

答案 1 :(得分:14)

使用以下语法:

Class<? extends SuperClass>[] avail = new Class[] { SubClass1.class, ... };

它会给你一个“未经检查”的警告,这是正确的,因为你可能包含一个Class对象,其类型不会在数组中扩展SuperClass

答案 2 :(得分:12)

使用数组执行此操作的正确方法是使用Collection执行此操作。抱歉!由于一系列复杂的原因,数组与泛型不匹配。数组具有与通用对象不同的协方差模型,这最终会导致您遇到的问题。例如,对于数组,但不是(通常)使用通用对象,您可以合法地执行此操作:

Object[] myArray = new String[5];

虽然你不能这样做:

LinkedList<Object> myCollection = new LinkedList<String>();

如果您想了解更多详细信息,可以看到优秀Arrays In Java Generics

中的Generics FAQ页面

正如simonn所说,您也可以按原样使用数组,并使用@SuppressWarnings("unchecked")来消除警告。这将起作用,但没有泛型可以为您提供的类型安全。如果您担心它的性能,只需使用ArrayList,这样您就可以在数组周围使用一个薄的包装器,但具有泛型提供的所有类型安全保证。

答案 3 :(得分:5)

  

但是用数组做这个的正确方法是什么?

没有类型安全的方法来做到这一点; 使用该集合是正确的方法。要了解原因,想象一下是否允许这样做。你可能会遇到这样的情况:

// Illegal!
Object[] baskets = new FruitBasket<? extends Citrus>[10];

// This is okay.
baskets[0] = new FruitBasket<Lemon>();

// Danger! This should fail, but the type system will let it through.
baskets[0] = new FruitBasket<Potato>();

类型系统需要检测添加到数组的篮子是FruitBasket<? extends Citrus>类型还是子类型。 FruitBasket不匹配,应该使用ArrayStoreException拒绝。但没有任何反应!

由于类型擦除,JVM只能看到数组的运行时类型。在运行时,我们需要将数组类型与元素类型进行比较,以确保它们匹配。类型擦除后,数组的运行时组件类型为FruitBasket[];同样,元素的运行时类型为FruitBasket。没有问题会被发现 - 这就是为什么这是危险的。

答案 4 :(得分:2)

问题是创建泛型类型的数组是非法的。解决它的唯一方法是在创建数组时转换为泛型类型,但这不是一个非常好的解决方案。 (请注意,可以使用通用数组,而不是创建一个:请参阅this question。)

你应该几乎总是使用列表而不是数组,所以我认为你已经提出了最好的解决方案。

答案 5 :(得分:0)

不安全且未经检查的投射警告:

Class<? extends SuperClass>[] availableTypes =
    (Class<? extends SuperClass>[])
    (new Class[]{ SubClass1.class, SubClass2.class });