在JDK1.5中,JAVA引入了泛型,同时引入了接口Type。我们常说的Class即是Type的实现类之一。Type可以分为两大类,如下图所示
先讲最常见的ParameterizedType,即参数泛型。 我们都知道在JAVA中,为了适配历史的jdk版本,java使用的机制是泛型擦除。即实际上泛型在JAVA内部都是以Object类型存储的。 如下:
public class MyTest {
public static void main(String[] args) throws NoSuchFieldException {
A a = new A();
//获取A类中所有字段
Field[] fields = a.getClass().getFields();
for (Field field : fields) {
//打印字段类型
System.out.println(field.getType());
}
}
}
class A<T> {
public T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
实际如下:
因此会有以下的情况
public class MyTest {
public static void main(String[] args) throws NoSuchFieldException {
A<String> a = new A();
a.setData("!23");
System.out.println(a.getData());
//这里得用b中间接收,因此如果直接设置a.setData放入int型会编译不通过,因为在编译期会进行类型检查。
A b = a;
b.setData(1);
System.out.println(b.getData());
}
}
实际如下
如果上述的最后一步b.getData()改成a.getData()。则会报如下错误
因此可以看出实际上泛型数据的数据类型是Object,只不过指定了泛型的类型之后,java会自动得帮我们进行类型转换。
那么我们如何通过类信息就能获取到泛型呢?
由于泛型擦除的原因,我们是无法直接通过自身的类信息获取到泛型类型的。不过如果我们继承参数泛型对象时指定参数类型时,即可以通过父类信息获取到泛型类型。 具体如下
public class MyTest {
public static void main(String[] args) throws NoSuchFieldException {
//区别于getSuperClass,getGenericSuperClass方法会保留泛型类型。
Type genericSuperclass = B.class.getGenericSuperclass();
//对应开头的图片,参数泛型的数据类型实际为ParameterizedType。
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
//获取所有泛型参数,因为泛型可能为多个,因此返回的是数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
//打印泛型类型
System.out.println(actualTypeArgument.getTypeName());
}
}
}
class A<T> {
public T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
class B extends A<String>{
}
实际如下:
那么ParameterizedType先告一段落了,接下来讲GenericArrayType,还是以上述代码进行改造,如下
public class MyTest {
public static void main(String[] args) throws NoSuchFieldException {
Field[] fields = B.class.getFields();
for (Field field : fields) {
//获取字段泛型类型
GenericArrayType genericArrayType= (GenericArrayType) field.getGenericType();
System.out.println(genericArrayType);
//获取泛型数组中的泛型
System.out.println(genericArrayType.getGenericComponentType());
}
}
}
class A<T> {
public T[] data;
public T[] getData() {
return data;
}
public void setData(T[] data) {
this.data = data;
}
}
class B extends A<String>{
}
打印如下
我们可以看到,这里打印的是T[]和T,而非之前的具体的数据类型,如String。只是因为之前讲过,实际上只有父类才保存了对应的泛型信息,但是我们这里直接拿的是B类字段的泛型类型。这里不采用第一种方式,是因为只获取父类中的泛型声明无法知道具体字段中究竟存的是T 还是T[] ,因此最好第二种的写法改成第一种,即如下
//虽然是存泛型数组,但是我们直接在泛型声明中声明成数组类型
class B extends A<String[]>{
}
那么按照第一种方式重新执行,则打印如下:
接下来讲下WildcardType的例子。
具体如下
public class MyTest {
public static void main(String[] args) throws NoSuchFieldException {
Field[] fields = A.class.getFields();
for (Field field : fields) {
//此处获取到List>
Type genericType = field.getGenericType();
//由于List本身是个泛型参数,因此我们先转成ParameterizedType。
ParameterizedType parameterizedType= (ParameterizedType)genericType;
//再从List中获取到具体的T
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
//由于此处使用通配符并说明该泛型继承自String,因此是WildcardType类型。
WildcardType wildcardType = (WildcardType) actualTypeArgument;
//获取所有上界
for (Type upperBound : wildcardType.getUpperBounds()) {
System.out.println(upperBound.getTypeName());
}
}
}
}
}
class A {
public List<? extends String> data;
public List<? extends String> getData() {
return data;
}
public void setData(List<? extends String> data) {
this.data = data;
}
}
打印如下
获取下界代码如下
public class MyTest {
public static void main(String[] args) throws NoSuchFieldException {
Field[] fields = A.class.getFields();
for (Field field : fields) {
Type genericType = field.getGenericType();
ParameterizedType parameterizedType= (ParameterizedType)genericType;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
WildcardType wildcardType = (WildcardType) actualTypeArgument;
for (Type upperBound : wildcardType.getLowerBounds()) {
System.out.println(upperBound.getTypeName());
}
}
}
}
}
class A {
public List<? super String> data;
public List<? super String> getData() {
return data;
}
public void setData(List<? super String> data) {
this.data = data;
}
}
打印如下
如果是想获取方法中的类型,具体如下
public class MyTest {
public static void main(String[] args) throws NoSuchFieldException {
Method[] methods = A.class.getDeclaredMethods();
for (Method method : methods) {
//获取方法中的参数类型(包含泛型,区别于getParameterTypes)
Type[] genericParameterTypes = method.getParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
//因为这里只有setData有方法有入参,因此直接对应List super String>类型是ParameterizedType
ParameterizedType parameterizedType = (ParameterizedType) genericParameterType;
System.out.println(parameterizedType);
//获取List中的泛型参数
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
//由于实际中是通配符?super String ,因此转成WildcardType
WildcardType wildcardType = (WildcardType) actualTypeArgument;
Type[] lowerBounds = wildcardType.getLowerBounds();
for (Type lowerBound : lowerBounds) {
System.out.println(lowerBound);
}
}
}
}
}
}
class A {
public List<? super String> data;
public List<? super String> getData() {
return data;
}
public void setData(List<? super String> data) {
this.data = data;
}
}
打印如下
以上获取泛型的数据类型如此复杂是因为我们是建立在通过类信息获取泛型类型导致的。如果我们已经将泛型所对应的数据放入对象中,那么实际上就可以按照普通获取class类获取类的方式获取。 具体如下
public class MyTest {
public static void main(String[] args) {
A<String> a = new A<>();
a.setData("123");
System.out.println(a.getType());
}
}
class A<T> {
public T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getType(){
return this.data.getClass().getTypeName();
}
}
打印如下