以下是如何使用泛型来获取您正在寻找的类型的数组,同时保持类型安全(与其他答案相反,这将给您返回一个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
充当Class
代表类的对象String
。这也适用于接口、枚举、任意维数组(例如String[].class
)、基元(例如int.class
)和关键字void
(即void.class
)。
Class
本身是泛型的(声明为Class<T>
,其中T
代表Class
对象所代表的类型),这意味着 的类型String.class
是Class<String>
。
因此,无论何时调用 for 的构造函数,都会GenSet
为第一个参数传入一个类文字,该参数表示实例声明类型的数组GenSet
(例如String[].class
for 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
表示 class 的对象String
,但它的类型是Class<?>
, not 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));
}