最近在做一个
Map
数据结构转换为指定
Class
类型的工具,涉及了反射相关的知识点。借这个机会,整理下这方面的知识。这篇文章先来了解下泛型和Type类型的知识点。
Java JDK从1.5开始引入泛型这个概念,在这之前只有原始类型而没有泛型。泛型实现了参数化类型的概念,使代码可以应用于多种类型。泛型这个术语的意思是指:“适用于许多许多类型“。但是Java这种泛型仅仅存在于编译阶段,在JVM运行的过程中,泛型相关信息将会被擦除。所以泛型只是Java语言的一个语法糖。比如
ListString
和
ListInteger
在运行时事实上是相同的类型。这两种形式都被擦除成它们的”原生“类型,即
List
。下面我们先来看下Java中泛型的使用姿势。
``(泛型变量)
Java的泛型可以应用在接口、类和方法上,平时看到了``、
K
、
V
这些泛型变量,也叫
类型变量(Type Variable)
,就表示这里声明了一个泛型。常见通用的的一些泛型变量名如下:
// 接口上声明了泛型变量K,V
public interface MapK,V {
// 方法上使用接口声明的泛型变量K,V
V put(K key, V value);
}
// 这里表示Collection和List使用的是同一个泛型变量E
public interface ListE extends CollectionE {
// 方法上使用接口声明的泛型变量E
E get(int index);
}
// 类或者接口上未声明泛型变量
public class QueryResponseUtils {
// 只在方法中使用泛型变量,方法前要加声明泛型变量
public QueryResponse success(T data) {
QueryResponse response = new QueryResponse();
response.setSucceeded(true);
response.setData(data);
return response;
}
}
?
(无限定通配符)
无限定通配符经常与容器类配合使用,其中
?
代表
未知类型
,即表示所有类型。当类型不确定的时候,可以使用。
public void testWildcard() {
printList(new ArrayListString());
printList(new ArrayListInteger());
}
public void printList(List? list) {
System.out.println(list);
}
但是涉及到
?
时的操作,一定与具体类型无关。比如
List
的
size()
方法。
public void addNewElement(List? list) {
// 编译失败,不能操作和类型相关的操作
list.add("abc");
// 只能添加null,因为null与类型无关
list.add(null);
}
public void printSize(List? list) {
// 操作和类型无关的方法,类似的还有remove等
System.out.println(list.size());
}
? extends
(有上限的通配符)
在这里,
?
代表类型
T
及
T
的子类,表示里面存的是
T及T的子类
。
如果需要读取
T
类型的元素,需要声明成
? extends
,取出的元素的类型是
T
。例如
List? extends Number
,表示
List
里面存的是
Number及其子类
。此时不能往列表中添加元素,取出的元素类型是
Number
。
public void addElement(List? extends Number list) {
// 编译失败
list.add(1);
}
public Number getElement(List? extends Number list) {
// 得到上限类型Number
Number number = list.get(0);
return number;
}
由于使用了上界限定,那么它保证其中的元素一定是
Number
,可以调用
get()
方法得到类型为
Number
的元素;
但是
? extends Number
只是限定了上界,表示是
Number
的子类,无法确定具体是哪个子类,故无法对其进行写入。对
? extends
来说,禁止所有的写入操作。
? super
(有下限的通配符)
在这里,
?
代表类型
T
及
T
的父类,表示里面存的是
T及T的父类
。如果需要添加
T
类型的元素,需要声明成
?super
,例如
List? super Number
,表示
List
里面存的是
Number
的父类,即
Object
。此时只能向其中添加
Number
及其子类。
从其中取元素的时候,要注意取出元素的类型是
Object
。
public void addElement1(List? super Number list) {
// 添加Number类型及其子类
list.add(1);
list.add(1.0);
}
public Object getElement1(List? super Number list) {
// 只能拿到Object类型
Object number = list.get(0);
return number;
}
由于使用了下界限定,那么需要保证写入的元素一定是
Number
及其子类,可以调用
add()
方法, 向其中添加
Integer
、
Double
、
Number
等元素,不能添加
Number
的父类。
取数据时,只能取到
? super
,相当于是没有上限的父类,那么就是
Object
。
T
与
?
的不同点
java.lang.reflect.Type
Type
是Java 1.5 为了引入泛型而新增的接口,是Java中所有类型的公共父接口,继承关系如下图:
如上图,
Type
主要包括四个子接口
ParameterizedType
、
TypeVariable
、
GenericArrayType
、
WildcardType
,以及最重要的实现类
Class
。下面来详细介绍这四种类型的使用:
java.lang.reflect.ParameterizedType
(参数化类型)
参数化类型代表通常的泛型类型,比如
CollectionString
、
MapString, Integer
。下面来看看它的三个方法:
需要注意的是,只返回最外层``的类型,里面嵌套的类型不返回,比如
MapString, MapString, String
,返回
[String.class, MapString, String]
。其中
MapString, String
也是一个
ParameterizedType
。
下面我们来看下具体的例子。
public class ParameterizedTypeTest {
private MapString, Integer map;
private Map.EntryString, String entry;
private MapString, MapString, String nestedMap;
private Field getField(Class? clazz, String fieldName) {
return ReflectionUtils.findField(ParameterizedTypeTest.class, fieldName);
}
@Test
public void testRawType() {
Field field = getField(ParameterizedTypeTest.class, "map");
Type genericType = field.getGenericType();
Assert.assertTrue(genericType instanceof ParameterizedType);
ParameterizedType parameterizedType = (ParameterizedType) genericType;
// 获取本身的类型
Assert.assertEquals(Map.class, parameterizedType.getRawType());
}
@Test
public void testOwnerType() {
Field field = getField(ParameterizedTypeTest.class, "entry");
Type genericType = field.getGenericType();
Assert.assertTrue(genericType instanceof ParameterizedType);
ParameterizedType parameterizedType = (ParameterizedType) genericType;
// 获取所在类的Type
Assert.assertEquals(Map.class, parameterizedType.getOwnerType());
}
@Test
public void testActualTypeArguments() {
Field field = getField(ParameterizedTypeTest.class, "map");
Type genericType = field.getGenericType();
Assert.assertTrue(genericType instanceof ParameterizedType);
ParameterizedType parameterizedType = (ParameterizedType) genericType;
// 获取实际参数类型
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Assert.assertEquals(String.class, actualTypeArguments[0]);
Assert.assertEquals(Integer.class, actualTypeArguments[1]);
}
@Test
public void testNestedActualTypeArguments() {
Field field = getField(ParameterizedTypeTest.class, "nestedMap");
Type genericType = field.getGenericType();
Assert.assertTrue(genericType instanceof ParameterizedType);
ParameterizedType parameterizedType = (ParameterizedType) genericType;
// 获取实际参数类型
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Assert.assertEquals(String.class, actualTypeArguments[0]);
// actualTypeArguments[1]也是参数化类型
Assert.assertTrue(actualTypeArguments[1] instanceof ParameterizedType);
Assert.assertEquals(Map.class, ((ParameterizedType)actualTypeArguments[1]).getRawType());
}
}
java.lang.reflect.TypeVariable
(类型变量)
类型变量指的是常见的
T
、
K
、
V
等这些范型变量。由于
Java
的泛型信息在编译时会被转换成一个特定的类型,而
TypeVariable
就有用来反映在虚拟机编译该泛型前的信息。以下是它拥有的具体方法:
public class TypeVariableTest {
class TypeVariableObjectK, V extends Number {
private K key;
private V value;
}
@Test
public void testTypeVariable() {
Field kField = ReflectionUtils.findField(TypeVariableObject.class, "key");
Field vField = ReflectionUtils.findField(TypeVariableObject.class, "value");
Type kGenericType = kField.getGenericType();
Type vGenericType = vField.getGenericType();
Assert.assertTrue(kGenericType instanceof TypeVariable);
Assert.assertTrue(vGenericType instanceof TypeVariable);
TypeVariable kTypeVariable = (TypeVariable) kGenericType;
TypeVariable vTypeVariable = (TypeVariable) vGenericType;
Assert.assertEquals("K", kTypeVariable.getName());
Assert.assertEquals("V", vTypeVariable.getName());
Assert.assertEquals(TypeVariableObject.class, kTypeVariable.getGenericDeclaration());
Assert.assertEquals(TypeVariableObject.class, vTypeVariable.getGenericDeclaration());
Assert.assertEquals(Object.class, kTypeVariable.getBounds()[0]);
Assert.assertEquals(Number.class, vTypeVariable.getBounds()[0]);
}
}
java.lang.reflect.GenericArrayType
(泛型数组类型)
组成数组的元素中有泛型,那么就实现了该接口,它的组成元素可以是
ParameterizedType
类型或者
TypeVariable
类型。如果元素不是泛型,那就是普通的
Class
类型。
public class GenericArrayTypeTest {
private class GenericComponentTypeObjectT {
private T[] typeVariableArray;
private MapString, String[] parameterizedTypeArray;
private Integer[] wrapperArray;
private int[] primitiveArray;
}
@Test
public void testGetGenericComponentType() {
// 元素为TypeVariable
Field typeVariableArrayField = ReflectionUtils.findField(GenericComponentTypeObject.class, "typeVariableArray");
Type typeVariableArrayGenericType = typeVariableArrayField.getGenericType();
Assert.assertTrue(typeVariableArrayGenericType instanceof GenericArrayType);
Type typeVariableArrayGenericComponentType = ((GenericArrayType) typeVariableArrayGenericType).getGenericComponentType();
Assert.assertTrue(typeVariableArrayGenericComponentType instanceof TypeVariable);
Assert.assertEquals("T", typeVariableArrayGenericComponentType.getTypeName());
// 元素为ParameterizedType
Field parameterizedTypeArrayField = ReflectionUtils.findField(GenericComponentTypeObject.class, "parameterizedTypeArray");
Type parameterizedTypeArrayGenericType = parameterizedTypeArrayField.getGenericType();
Assert.assertTrue(parameterizedTypeArrayGenericType instanceof GenericArrayType);
Type parameterizedTypeArrayGenericComponentType = ((GenericArrayType) parameterizedTypeArrayGenericType).getGenericComponentType();
Assert.assertTrue(parameterizedTypeArrayGenericComponentType instanceof ParameterizedType);
// 元素为原生基本类型
Field primitiveArrayField = ReflectionUtils.findField(GenericComponentTypeObject.class, "primitiveArray");
Type primitiveArrayGenericType = primitiveArrayField.getGenericType();
Assert.assertTrue(primitiveArrayGenericType instanceof Class);
Assert.assertEquals(int.class, ((Class) primitiveArrayGenericType).getComponentType());
// 元素为普通Class类型
Field wrapperArrayField = ReflectionUtils.findField(GenericComponentTypeObject.class, "wrapperArray");
Type wrapperArrayGenericType = wrapperArrayField.getGenericType();
Assert.assertTrue(wrapperArrayGenericType instanceof Class);
Assert.assertEquals(Integer.class, ((Class) wrapperArrayGenericType).getComponentType());
}
}
java.lang.reflect.WildcardType
(通配符类型)
通配符类型,所有带
?
的类型,比如
?
、
? extend Number
。
WildcardType
包含两个方法,如下:
public class WildcardTypeTest {
private class WildcardTypeObject {
private List? wildcardList;
private List? extends Number upperBoundList;
private List? super Number lowerBoundList;
}
@Test
public void testGetGenericComponentType() {
Field wildcardListField = ReflectionUtils.findField(WildcardTypeObject.class, "wildcardList");
Type wildcardListGenericType = wildcardListField.getGenericType();
Type wildcardListActualType= ((ParameterizedType) wildcardListGenericType).getActualTypeArguments()[0];
Assert.assertTrue(wildcardListActualType instanceof WildcardType);
Field upperBoundListField = ReflectionUtils.findField(WildcardTypeObject.class, "upperBoundList");
Type upperBoundListGenericType = upperBoundListField.getGenericType();
Type upperBoundListActualType= ((ParameterizedType) upperBoundListGenericType).getActualTypeArguments()[0];
Assert.assertTrue(upperBoundListActualType instanceof WildcardType);
Assert.assertEquals(Number.class, ((WildcardType) upperBoundListActualType).getUpperBounds()[0]);
Assert.assertEquals(0, ((WildcardType) upperBoundListActualType).getLowerBounds().length);
Field lowerBoundListField = ReflectionUtils.findField(WildcardTypeObject.class, "lowerBoundList");
Type lowerBoundListGenericType = lowerBoundListField.getGenericType();
Type lowerBoundListActualType= ((ParameterizedType) lowerBoundListGenericType).getActualTypeArguments()[0];
Assert.assertTrue(lowerBoundListActualType instanceof WildcardType);
Assert.assertEquals(Number.class, ((WildcardType) lowerBoundListActualType).getLowerBounds()[0]);
Assert.assertEquals(Object.class, ((WildcardType) lowerBoundListActualType).getUpperBounds()[0]);
}
}
原文始发于微信公众号(xiaogan的技术博客):