这是在保留类型安全性的同时,使用泛型获取与您正在寻找的类型完全相同的数组的方法(与其他答案相反,其他答案将为您提供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,对于声明GenSetas 实例的任何类型,都可以将其分配a给该类型的数组,并且可以将元素从分配给a该类型的变量,这意味着该数组并且数组中的值是正确的类型。
如Java教程中所述,它通过将类文字用作运行时类型标记来工作。类文字被编译器视为的实例java.lang.Class。要使用一个,只需在类名后面加上即可.class。因此,String.class充当Class表示class 的对象String。这也适用于接口,枚举,任意维数组(例如String[].class),基元(例如int.class)和关键字void(例如void.class)。
Class本身是通用的(声明为Class<T>,其中T代表Class对象表示的类型),表示的类型String.class为Class<String>。
因此,每当您为调用构造函数时GenSet,您都将为第一个参数传入一个类常量,以表示GenSet实例的声明类型的数组(例如,String[].class针对GenSet<String>)。请注意,您将无法获得基元数组,因为基元不能用于类型变量。
在构造函数内部,调用方法cast将传递的Object参数转换为由调用该方法的Class对象表示的类。调用静态方法newInstance在java.lang.reflect.Array返回作为Object由所表示的类型的数组Class作为第一个参数,并通过指定的长度的传递的对象int通过作为第二个参数。调用该方法getComponentType将返回一个Class对象,该对象表示该方法所调用的对象所代表的数组的组件类型Class(例如,String.class对于String[].class,null如果该Class对象不代表数组)。
最后一句话并不完全正确。调用String[].class.getComponentType()返回Class代表该类的对象String,但其类型为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));
}