这是在保留类型安全性的同时,使用泛型获取与您正在寻找的类型完全相同的数组的方法(与其他答案相反,其他答案将为您提供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
as 实例的任何类型,都可以将其分配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));
}