如何正确使用泛型类型的数组?

时间:2009-05-22 10:32:00

标签: java generics

我有一个类根据消息的类将传入的消息映射到匹配的阅读器。所有消息类型都实现接口消息。读者在mapper类中注册,说明它将能够处理哪些消息类型。这些信息需要以某种方式存储在消息阅读器中,我的方法是从构造函数中设置private final数组。

现在,似乎我对泛型和/或数组有一些误解,我似乎无法弄清楚,请参阅下面的代码。它是什么?

public class HttpGetMessageReader implements IMessageReader {
    // gives a warning because the type parameter is missing
    // also, I actually want to be more restrictive than that
    // 
    // private final Class[] _rgAccepted;

    // works here, but see below
    private final Class<? extends IMessage>[] _rgAccepted;

    public HttpGetMessageReader()
    {
        // works here, but see above
        // this._rgAccepted = new Class[1];

        // gives the error "Can't create a generic array of Class<? extends IMessage>"
        this._rgAccepted = new Class<? extends IMessage>[1];

        this._rgAccepted[0] = HttpGetMessage.class;
    }
}

ETA 正如cletus正确指出的那样,最基本的Google搜索显示Java不允许通用数组。对于给出的示例我肯定理解这一点(例如E[] arr = new E[8],其中E是周围类的类型参数)。但为什么允许new Class[n]?那么“正确”(或至少是常见的)方式是什么呢?

5 个答案:

答案 0 :(得分:30)

Java不允许generic arraysJava Generics FAQ

中的更多信息

要回答您的问题,只需使用List(可能是ArrayList)而不是数组。

可以在Java theory and practice: Generics gotchas中找到更多解释:

  

泛型不协变

     

虽然你可能会觉得它很有帮助   将集合视为一种集合   数组的抽象,他们有一些   集合所做的特殊属性   不。 Java语言中的数组是   协变 - 这意味着如果   整数扩展数字(它   确实如此),那么不仅仅是Integer   也是一个数字,但Integer[]是   也是Number[],您可以自由选择   传递或分配Integer[]其中a   需要Number[]。 (更多   正式地说,如果Number是超类型   Integer的{​​{1}},然后是Number[]   Integer[]的超类型。)你可以   认为通用也是如此   类型 - List<Number>   是List<Integer>的超类型,和   您可以通过List<Integer>   预计会List<Number>的地方。   不幸的是,它不起作用   方式。

     

事实证明这是有充分理由的   不这样做:它会破裂   假设类型安全仿制药   提供。想象一下,你可以分配一个   List<Integer>List<Number>。   然后,以下代码将允许   你要放一些不是的东西   IntegerList<Integer>

List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));
     

因为ln是List<Number>,所以添加   一个Float似乎完全合法。   但如果ln与li混淆,那么   它会破坏类型安全承诺   隐含在li的定义中 -   它是一个整数列表,其中   这是泛型不可能的原因   协变。

答案 1 :(得分:4)

克莱图斯说的是对的。通用类型参数的通常强制不变性与Java数组的协方差之间存在一般性不匹配。

(一些背景:方差指定类型在子类型方面如何相互关联。即由于泛型类型参数不变 收集&lt ;:收藏不成立。因此,关于Java类型系统,String集合不是CharSequence集合。具有协变性的数组意味着对于任何类型T和U,其中T <:U,T []&lt ;:U []。因此,您可以将T []类型的变量保存到U []类型的变量中。由于自然需要其他形式的差异,Java至少允许使用通配符来实现这些目的。)

我经常使用的解决方案(实际上是hack)是声明一个生成数组的辅助方法:

public static <T> T[] array(T...els){
    return els;
}
void test(){
    // warning here: Type safety : A generic array of Class is created for a varargs parameter
    Class<? extends CharSequence>[] classes = array(String.class,CharSequence.class);
}

由于擦除,生成的数组将始终为Object []类型。

答案 2 :(得分:3)

  

那么“正确”(或者至少是常见的)方式是什么呢?

@SuppressWarnings(value="unchecked")
public <T> T[] of(Class<T> componentType, int size) {
    return (T[]) Array.newInstance(componentType, size);
}

public demo() {
    Integer[] a = of(Integer.class, 10);
    System.out.println(Arrays.toString(a));
}

答案 3 :(得分:1)

数组总是具有特定类型,不像集合在Generics之前的情况。

而不是

Class<? extends IMessage>[] _rgAccepted;

你应该写一下

IMessage[] _rgAccepted;

泛型不会进入它。

答案 4 :(得分:1)

IMHO,

this._rgAccepted = (Class<? extends IMessage>[])new Class[1];

处理此问题的适当方法。数组组件类型必须是具体类型,Class是与Class<whatever>最接近的具体类型。它会像你期望Class<whatever>[]一样工作。

是的,从技术上来说它是未经检查的,如果你将它转换为另一种更通用的数组类型并在其中放入错误的东西,可能会导致问题,但由于这是你班级中的私有字段,我认为你可以确定它是适当使用。

相关问题