下面是如何使用泛型来获得你正在寻找的精确类型的数组,同时保持类型安全(与其他答案相反,后者要么会返回Object数组,要么会在编译时产生警告):
import java.lang.reflect.Array;
public class GenSet<E> {
private E[] a;
public GenSet(Class<E[]> clazz, int length) {
a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));
}
public static void main(String[] args) {
GenSet<String> foo = new GenSet<String>(String[].class, 1);
String[] bar = foo.a;
foo.a[0] = "xyzzy";
String baz = foo.a[0];
}
}
编译时没有警告,正如你在main中看到的,无论你声明GenSet的实例为什么类型,你都可以将a赋值给该类型的数组,你也可以将a中的元素赋值给该类型的变量,这意味着数组和数组中的值都是正确的类型。
它通过使用类文字作为运行时类型标记来工作,如Java教程中讨论的那样。类字面量被编译器视为java.lang.Class的实例。要使用它,只需在类名后面加上.class。因此,String. Class充当了表示类String的Class对象。这也适用于接口、枚举、任何维度的数组(例如String[].class)、原语(例如int.class)和关键字void(例如void.class)。
Class本身是泛型的(声明为Class<T>,其中T代表Class对象所表示的类型),这意味着String. Class的类型是Class<String>。
因此,无论何时调用GenSet的构造函数,您都要传入一个类字面量作为表示GenSet实例声明类型的数组的第一个参数(例如String[].class for GenSet<String>)。请注意,您将无法获得一个原语数组,因为原语不能用于类型变量。
Inside the constructor, calling the method cast returns the passed Object argument cast to the class represented by the Class object on which the method was called. Calling the static method newInstance in java.lang.reflect.Array returns as an Object an array of the type represented by the Class object passed as the first argument and of the length specified by the int passed as the second argument. Calling the method getComponentType returns a Class object representing the component type of the array represented by the Class object on which the method was called (e.g. String.class for String[].class, null if the Class object doesn't represent an array).
最后一句不完全准确。调用String[].class.getComponentType()返回一个表示类String的Class对象,但它的类型是Class<?>,而不是Class<String>,这就是为什么你不能做下面这样的事情。
String foo = String[].class.getComponentType().cast("bar"); // won't compile
对于Class中返回Class对象的每个方法也是如此。
关于Joachim Sauer对这个答案的评论(我自己没有足够的声誉来评论它),使用转换为T[]的示例将导致一个警告,因为在这种情况下编译器不能保证类型安全。
编辑关于Ingo的评论:
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}