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

ski*_*ppy 47 java generics

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

Class<? extends SuperClass>[] availableTypes = { SubClass1.class, SubClass2.class };
Run Code Online (Sandbox Code Playgroud)

这给了我错误:

Cannot create a generic array of Class<? extends SuperClass>.
Run Code Online (Sandbox Code Playgroud)

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

Class<? extends SuperClass>[] availableTypes = Class<? extends SuperClass>[] { SubClass1.class, SubClass2.class };
Run Code Online (Sandbox Code Playgroud)

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

Class[] availableTypes = { SubClass1.class, SubClass2.class };
Run Code Online (Sandbox Code Playgroud)

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

类是原始类型.应参数化对泛型类的引用.

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

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

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

List<Class<? extends SuperClass>> availableTypes = new List<Class<? extends SuperClass>>();
Run Code Online (Sandbox Code Playgroud)

但是使用数组做到这一点的正确方法是什么?

Sim*_*son 23

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

两个解决方法:

  • 坚持使用集合(例如ArrayList<Class<? extends SuperClass>>),它与阵列一样工作,也允许扩展.
  • @SuppressWarnings("unchecked")注释中的代码创建与评论证明其使用沿着阵列.


eri*_*son 14

使用以下语法:

Class<? extends SuperClass>[] avail = new Class[] { SubClass1.class, ... };
Run Code Online (Sandbox Code Playgroud)

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


Edd*_*die 12

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

Object[] myArray = new String[5];
Run Code Online (Sandbox Code Playgroud)

虽然你不能这样做:

LinkedList<Object> myCollection = new LinkedList<String>();
Run Code Online (Sandbox Code Playgroud)

如果您想了解更多详细信息,可以从优秀的Generics常见问题解答中看到Arrays In Java Generics页面

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


Joh*_*lla 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>();
Run Code Online (Sandbox Code Playgroud)

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

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