以下是如何使用泛型来获取您正在寻找的类型的数组,同时保持类型安全(与其他答案相反,这将返回一个 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 Tutorials 中所讨论的,它通过使用类文字作为运行时类型标记来工作。编译器将类文字视为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 用于GenSet<String>)。请注意,您将无法获得基元数组,因为基元不能用于类型变量。
在构造函数内部,调用cast 方法会将传递的Object 参数强制转换为由调用该方法的Class 对象表示的类。在java.lang.reflect.Array 中调用静态方法newInstance 以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 对 this answer 的评论(我自己没有足够的声誉来评论它),使用强制转换为 T[] 的示例将导致警告,因为编译器无法保证类型安全那个案例。
编辑 Ingo 的 cmets:
public static <T> T[] newArray(Class<T[]> type, int size) {
return type.cast(Array.newInstance(type.getComponentType(), size));
}