Java反射的那些事- 泛型和类型

最近在做一个Map数据结构转换为指定Class类型实例的工具,涉及了大量反射相关的操作。借这个机会,整理下这方面的知识。

Java JDK从1.5开始引入泛型这个概念,在这之前只有原始类型而没有泛型。泛型实现了参数化类型的概念,使代码可以应用于多种类型。泛型这个术语的意思是指:“适用于许多许多类型“。但是Java这种泛型仅仅存在于编译阶段,在JVM运行的过程中,泛型相关信息将会被擦除。所以泛型只是Java语言的一个语法糖。比如List<String>List<Integer>在运行时事实上是相同的类型。这两种形式都被擦除成它们的”原生“类型,即List。下面我们先来看下Java中泛型的使用姿势。


泛型


<T>(泛型变量)

Java的泛型可以应用在接口、类和方法上,平时看到了<T><K><V>这些泛型变量,也叫类型变量(Type Variable),就表示这里声明了一个泛型。常见通用的的一些泛型变量名如下:

  • T(Type),表示Type(类型),常用的泛型变量名;
  • K(Key)V(Value),表示键值,通常用在Map里;
  • E(Element),表示元素,通常用在Collection中;
  • N(Number),表示数字;
  • 泛型变量名可以完全自定义,大小写不限、字母个数不限,但是为了规范,最好采用通用的泛型变量名;
// 接口上声明了泛型变量K,V
public interface Map<K,V> {
        // 方法上使用接口声明的泛型变量K,V
    V put(K key, V value);
}
// 这里表示Collection和List使用的是同一个泛型变量E
public interface List<E> extends Collection<E> {
   // 方法上使用接口声明的泛型变量E
   E get(int index);
}
// 类或者接口上未声明泛型变量
public class QueryResponseUtils {
    // 只在方法中使用泛型变量,方法前要加<T>声明泛型变量
    public <T> QueryResponse<T> success(T data) {
        QueryResponse<T> response = new QueryResponse<>();
        response.setSucceeded(true);
        response.setData(data);
        return response;
    }
}

Copy


<?>(无限定通配符)

无限定通配符经常与容器类配合使用,其中?代表未知类型,即表示所有类型。 当类型不确定的时候,可以使用。

    public void testWildcard() {
        printList(new ArrayList<String>());
        printList(new ArrayList<Integer>());
    }

    public void printList(List<?> list) {
        System.out.println(list);
    }

Copy

但是涉及到?时的操作,一定与具体类型无关。 比如Listsize()方法。

    public void addNewElement(List<?> list) {
        // 编译失败,不能操作和类型相关的操作
        list.add("abc");
        //  只能添加null,因为null与类型无关
        list.add(null);
    }
    public void printSize(List<?> list) {
        // 操作和类型无关的方法,类似的还有remove等
        System.out.println(list.size());
    }

Copy


<? extends T>(有上限的通配符)

在这里,? 代表类型TT的子类,表示里面存的是T及T的子类

如果需要读取T类型的元素,需要声明成<? extends T>,取出的元素的类型是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;
    }

Copy

由于使用了上界限定,那么它保证其中的元素一定是Number,可以调用get()方法得到类型为Number的元素;

但是? extends Number只是限定了上界,表示是Number的子类,无法确定具体是哪个子类,故无法对其进行写入。 对? extends来说,禁止所有的写入操作。


<? super T>(有下限的通配符)

在这里,? 代表类型TT的父类,表示里面存的是T及T的父类

如果需要添加T类型的元素,需要声明成<?super T>,例如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;
    }

Copy

由于使用了下界限定,那么需要保证写入的元素一定是Number及其子类,可以调用add()方法, 向其中添加IntegerDoubleNumber等元素,不能添加Number的父类。

取数据时,只能取到<? super T>,相当于是没有上限的父类,那么就是Object


T?的不同点

  1. 类型参数T是一个确定的类型,而通配符?表示一个不确定的类型。
  2. T只有extends一种限定方式,<T extends List>是合法的,<T super List>是不合法的。
  3. extendssuper两种限定方式,即<? extends List> 与<? super List>都是合法的。
  4. T可用于多重限定,如 <T extends A & B>,通配符不能进行多重限定。

类型存储读取写入实例TTTTList<String>?unknownObject除了null,其他无法写入List<?>? extends TT及子类T除了null,其他无法写入List<? extends Number>? super TT及父类ObjectT及其子类List<? super Number>


java.lang.reflect.Type

Type是Java 1.5 为了引入泛型而新增的接口,是Java中所有类型的公共父接口:


Type主要包括四个子接口ParameterizedTypeTypeVariableGenericArrayTypeWildcardType,以及最重要的实现类Class。 下面来详细介绍这四种类型的使用:


ParameterizedType(参数化类型)

参数化类型代表通常的泛型类型,比如Collection<String>Map<String, Integer>。下面来看看它的三个方法:


  • Type getRawType()
  • 该方法是获取当前参数化类型的类型,比如上面的Map<String, Integer>,返回的是Map.class
  • Type getOwnerType()
  • 该方法是获取当前参数化类型所在类的类型,比如Map.Entry<String, String>,返回是Map.class
  • Type[] getActualTypeArguments()
  • 该方法是获取当前参数化类型的实际类型数组,即<>里面的按顺序排列的所有类型,比如Map<String, Integer>,返回的是[String.class, Integer.class]

需要注意的是,只返回最外层<>的类型,里面嵌套的类型不返回,比如Map<String, Map<String, String>>,返回[String.class, Map<String, String>]。 其中Map<String, String>也是一个ParameterizedType

下面我们来看下具体的例子。

public class ParameterizedTypeTest {

    private Map<String, Integer> map;

    private Map.Entry<String, String> entry;

    private Map<String, Map<String, 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());
    }
}

Copy


TypeVariable(类型变量)

类型变量指的是常见的TKV等这些范型变量。由于Java的泛型信息在编译时会被转换成一个特定的类型,而TypeVariable就有用来反映在虚拟机编译该泛型前的信息。 以下是它拥有的具体方法:


  • Type[] getBounds()
  • 获取当前类型的上边界数组(可存在多个,因为extends可用&限定多个上边界),如果没有指定上边界,则默认为Object
  • D getGenericDeclaration()
  • 返回当前类型所在的类的Type;
  • String getName() 获取当前类型的名称;
public class TypeVariableTest {
    class TypeVariableObject<K, 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]);
    }
}

Copy


GenericArrayType(泛型数组类型)

组成数组的元素中有泛型,那么就实现了该接口,它的组成元素可以是ParameterizedType类型或者TypeVariable类型。 如果元素不是泛型,那就是普通的Class类型。


  • Type getGenericComponentType()
  • 获取泛型数组的实际参数化类型,比如Map[],就返回Map.class
public class GenericArrayTypeTest {

private class GenericComponentTypeObject<T> {
    private T[] typeVariableArray;
    private Map<String, 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());
}
}
  • Copy


WildcardType(通配符类型)

  • 通配符类型,所有带?的类型,比如<?><? extend Number>。 WildcardType包含两个方法,如下:

  • Type[] getUpperBounds()
  • 获取上边界类型的数组。 如果没有限定上边界,则上边界为Object.class,返回数组Type[Object.class]
  • Type[] getLowerBounds()
  • 获取下边界类型的数组。如果没有限定下边界,则返回空数组Type[0]
  • 下面我们来看下具体的例子。
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]);
}
}


评论区
Rick ©2018