由于Java泛型的实现,你不能有这样的代码:

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
    }
}

如何在保持类型安全的同时实现这一点?

我在Java论坛上看到一个解决方案是这样的:

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

但我真的不明白这是怎么回事。


当前回答

我必须反过来问一个问题:你的发电机组是“检查”还是“未检查”? 这是什么意思?

Checked: strong typing. GenSet knows explicitly what type of objects it contains (i.e. its constructor was explicitly called with a Class<E> argument, and methods will throw an exception when they are passed arguments that are not of type E. See Collections.checkedCollection. -> in that case, you should write: public class GenSet<E> { private E[] a; public GenSet(Class<E> c, int s) { // Use Array native method to create array // of a type only known at run time @SuppressWarnings("unchecked") final E[] a = (E[]) Array.newInstance(c, s); this.a = a; } E get(int i) { return a[i]; } } Unchecked: weak typing. No type checking is actually done on any of the objects passed as argument. -> in that case, you should write public class GenSet<E> { private Object[] a; public GenSet(int s) { a = new Object[s]; } E get(int i) { @SuppressWarnings("unchecked") final E e = (E) a[i]; return e; } } Note that the component type of the array should be the erasure of the type parameter: public class GenSet<E extends Foo> { // E has an upper bound of Foo private Foo[] a; // E erases to Foo, so use Foo[] public GenSet(int s) { a = new Foo[s]; } ... }

所有这些都是由于Java中泛型的一个已知且刻意的弱点:它是使用擦除来实现的,因此“泛型”类不知道它们在运行时是用什么类型参数创建的,因此不能提供类型安全,除非实现了一些显式机制(类型检查)。

其他回答

一个简单的解决方法是在主类中嵌套第二个“holder”类,并使用它来保存数据。

public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}

Java泛型的工作原理是在编译时检查类型并插入适当的类型转换,但在编译后的文件中删除类型。这使得不理解泛型的代码(这是一个深思熟虑的设计决策)可以使用泛型库,但这意味着您通常无法在运行时找到类型。

公共Stack(Class<T> clazz,int capacity)构造函数要求您在运行时传递Class对象,这意味着类信息在运行时可用于需要它的代码。而Class<T>形式意味着编译器会检查你传递的Class对象是否恰好是类型T的Class对象,不是T的子类,也不是T的超类,而是恰好是T。

这意味着您可以在构造函数中创建适当类型的数组对象,这意味着您存储在集合中的对象的类型将在它们被添加到集合时检查它们的类型。

我找到了一个解决这个问题的方法。

下面这行代码抛出泛型数组创建错误

List<Person>[] personLists=new ArrayList<Person>()[10];

但是,如果我将List<Person>封装在一个单独的类中,它就可以工作。

import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}

您可以通过getter在PersonList类中公开人员。下面的行将给出一个数组,每个元素中都有List<Person>。也就是说数组List<Person>。

PersonList[] personLists=new PersonList[10];

我需要这样的东西在一些代码中,我正在工作,这就是我所做的让它工作。到目前为止还没有问题。

在java中不允许创建通用数组,但是你可以这样做

class Stack<T> {
private final T[] array;
public Stack(int capacity) {
    array = (T[]) new Object[capacity];
 }
}

这在Effective Java第二版第5章(泛型)第25项中有介绍…更喜欢列表而不是数组

你的代码将会工作,尽管它会生成一个未检查的警告(你可以用下面的注释来抑制它):

@SuppressWarnings({"unchecked"})

然而,使用List而不是Array可能会更好。

在OpenJDK项目网站上有一个关于这个bug/特性的有趣讨论。