如何使用类的数组generics?

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

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

这给了我错误:

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

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

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

如果我消除了generics限定条件,我可以获得代码进行编译:

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

但是,我得到了generics警告:

类是一个原始types。 generics类的引用应该被参数化。

我尝试着; 我尝试着! :)另外,在这一点上,即使这不会引发警告,我也会失去一块我正在试图定义的界面。 我不想只返回一个任意类的数组; 我想返回一个特定的SuperClass的所有子类的类的数组!

Eclipse有一些非常强大的工具来确定用来修正generics声明的参数,但是在这种情况下,它会下降,因为它在处理Class时往往会这样做。 它提供的“推断genericstypes参数”过程根本不会改变代码,而是留下警告。

我能够通过使用一个集合来解决这个问题:

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

但是用数组来做这件事的正确方法是什么?

这似乎有点失败,但这样的问题正是大多数人避免混合arrays和generics的原因。 由于generics的实现方式( types擦除 ),数组和generics将永远不能很好地协同工作。

两种解决方法:

  • 坚持使用一个集合(例如ArrayList<Class<? extends SuperClass>> ),它可以像数组一样工作,也允许扩展。
  • 在创build数组的代码上添加一个@SuppressWarnings("unchecked")注释以及评论它的用法。

使用这个语法:

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

它会给你一个“未经检查”的警告,正确的是,因为你可能包括一个Class对象的types没有扩展SuperClass中的SuperClass

用数组来做这件事的正确方法是用一个Collection来完成。 抱歉! 由于一系列复杂的原因,数组不能很好地处理generics。 数组与通用对象有不同的协方差模型,最终导致您遇到的问题。 例如,对于数组,但不是(通常)使用generics对象,则可以合法地执行此操作:

 Object[] myArray = new String[5]; 

而你不能这样做:

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

如果你想了解更多的细节,可以看看Generics常见问题解答中的“ Javagenerics数组”一章

正如simonn所说,你也可以直接使用你的数组,并使用@SuppressWarnings("unchecked")来使警告@SuppressWarnings("unchecked") 。 这将起作用,但是没有generics可以提供的types安全性。 如果你担心的是性能,那么只需要使用一个ArrayList这样你只需要在数组中使用一个薄包装器,但是generics提供了所有的types安全保证。

但是用数组来做这件事的正确方法是什么?

没有types安全的方法来做到这一点; 使用集合是正确的方法。 看看为什么,想象一下,如果这是允许的。 你可能有这样的情况:

 // 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>(); 

types系统需要检测添加到数组中的篮子是否为FruitBasket<? extends Citrus>typesFruitBasket<? extends Citrus> FruitBasket<? extends Citrus>或一个亚型。 FruitBasket不匹配,应该被ArrayStoreException拒绝。 但没有任何反应!

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

问题是创build一个genericstypes的数组是非法的。 解决这个问题的唯一方法是在创build数组时通过转换为genericstypes,但这不是一个很好的解决scheme。 (请注意,可以使用通用数组,而不是创build一个:请参阅此问题 。)

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

不能安全的input警告

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