Java Type与Generic

Type 是什么

Type is the common superinterface for all types in the Java programming language. These include raw types, parameterized types, array types, type variables and primitive types.

这个Type接口是在1.5后才被引入的,子类有GenericArrayType、ParameterizedType、WildcardType、TypeVaribale,在1.5之前,没有这个Type,全部都是Class,引入的主要原因是由于泛型的引入,其他的子类除了Class都是在1.5的时候一起引入的。

Class

Instances of the class Class represent classes and interfaces in a running Java application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions. The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects.

在没有泛型之前,java中的所有的类(包括枚举)、接口(包括注解)、原生的数据类型都是class。

Class has no public constructor. Instead Class objects are constructed automatically by the Java Virtual Machine as classes are loaded and by calls to the defineClass method in the class loader.

所有的类对象都是类加载器加载的。

        System.out.println(String.class.getName()); // java.lang.String
        System.out.println(int.class.getName()); // int
        System.out.println(Runnable.class.getName()); // java.lang.Runnable
        System.out.println(Nullable.class.getName()); // 注解 com.sun.istack.internal.Nullable 
		System.out.println(String.class instanceof Type); // true

ParameterizedType

ParameterizedType represents a parameterized type such as Collection. A parameterized type is created the first time it is needed by a reflective method, as specified in this package. When a parameterized type p is created, the generic type declaration that p instantiates is resolved, and all type arguments of p are created recursively. See TypeVariable for details on the creation process for type variables. Repeated creation of a parameterized type has no effect.

指具体的泛型类型,参数化的类型,参数就是指下面的TypeVariable。注意,这里Parameterized这个修饰,在java中也可以定义Map m;这样的map(不推荐,参考Effective Java中的请不要在新代码中使用原生态类型row type),这类型没有参数,也不是ParameterizedType。

public class TestType  {
    Map<String, String> map;
    Map mapNoPara;
    public static void main(String[] args) throws Exception {
        Field f = TestType.class.getDeclaredField("map");
        System.out.println(f.getGenericType());                               // java.util.Map<java.lang.String, java.lang.String>
        System.out.println(f.getGenericType() instanceof ParameterizedType);  // true
        ParameterizedType pType = (ParameterizedType) f.getGenericType();
        System.out.println(pType.getRawType());                               // interface java.util.Map
        for (Type type : pType.getActualTypeArguments()) {
            System.out.println(type);                                         // 打印两遍: class java.lang.String
        }

        Field f2 = TestType.class.getDeclaredField("mapNoPara");
        System.out.println(f2.getGenericType() instanceof ParameterizedType);  // false

    }
}

TypeVariable

TypeVariable is the common superinterface for type variables of kinds. A type variable is created the first time it is needed by a reflective method, as specified in this package. If a type variable t is referenced by a type (i.e, class, interface or annotation type) T, and T is declared by the nth enclosing class of T (see JLS 8.1.2), then the creation of t requires the resolution (see JVMS 5) of the ith enclosing class of T, for i = 0 to n, inclusive. Creating a type variable must not cause the creation of its bounds. Repeated creation of a type variable has no effect.

可以理解为泛型中的T、E这些,这个类型里面有该类型bounds。

public class TestType <K extends Comparable & Serializable, V> {
    K key;
    V value;

    public static void main(String[] args) throws Exception {
        // 获取字段的类型
        Field fk = TestType.class.getDeclaredField("key");
        Field fv = TestType.class.getDeclaredField("value");
        System.out.println( "TypeVariable类型" + (fk.getGenericType() instanceof TypeVariable)); // true
        System.out.println( "TypeVariable类型" + (fv.getGenericType() instanceof TypeVariable)); // true
        TypeVariable keyType = (TypeVariable)fk.getGenericType();
        TypeVariable valueType = (TypeVariable)fv.getGenericType();
        // getName 方法
        System.out.println(keyType.getName());                 // K
        System.out.println(valueType.getName());               // V
        // getGenericDeclaration 方法
        System.out.println(keyType.getGenericDeclaration());   // class com.test.TestType
        System.out.println(valueType.getGenericDeclaration()); // class com.test.TestType
        // getBounds 方法
        System.out.println("K 的上界:");                        // 有两个
        for (Type type : keyType.getBounds()) {                // interface java.lang.Comparable
            System.out.println(type);                          // interface java.io.Serializable
        }
        System.out.println("V 的上界:");                        // 没明确声明上界的, 默认上界是 Object
        for (Type type : valueType.getBounds()) {              // class java.lang.Object
            System.out.println(type);
        }
    }
}

GenericArrayType

GenericArrayType represents an array type whose component type is either a parameterized type or a type variable.

泛型数组类型,类型中有泛型,还必须是数组

    public static void main(String[] args) throws Exception {
        Method method = Test.class.getDeclaredMethods()[0];
        // public void com.test.Test.show(java.util.List[],java.lang.Object[],java.util.List,java.lang.String[],int[])
        System.out.println(method);
        Type[] types = method.getGenericParameterTypes();
        for (Type type : types) {
            System.out.println(type instanceof GenericArrayType);
        }
    }

    class Test<T> {
        public void show(List<String>[] pTypeArray, // List<String>是ParameterizedType类型 true
                         T[] vTypeArray, // T是TypeVariable类型,true
                         List<String> list, 
                         String[] strings, // String是普通对象, 没有范型,false
                         int[] ints) {
        }
    }

WildcardType

WildcardType represents a wildcard type expression, such as ?, ? extends Number, or ? super Integer. 该接口表示通配符泛型, 比如? extends Number 和 ? super Integer

public class TestType {
    private List<? extends Number> a;  // // a没有下界, 取下界会抛出ArrayIndexOutOfBoundsException
    private List<? super String> b;
    public static void main(String[] args) throws Exception {
        Field fieldA = TestType.class.getDeclaredField("a");
        Field fieldB = TestType.class.getDeclaredField("b");
        // 先拿到范型类型
        System.out.println(fieldA.getGenericType() instanceof ParameterizedType); // true
        System.out.println(fieldB.getGenericType() instanceof ParameterizedType); // true
        ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
        ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
        // 再从范型里拿到通配符类型
        System.out.println(pTypeA.getActualTypeArguments()[0] instanceof WildcardType); // true
        System.out.println(pTypeB.getActualTypeArguments()[0] instanceof WildcardType); // true
        WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];
        WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];
        // 方法测试
        System.out.println(wTypeA.getUpperBounds()[0]);   // class java.lang.Number
        System.out.println(wTypeB.getLowerBounds()[0]);   // class java.lang.String
        // 看看通配符类型到底是什么, 打印结果为: ? extends java.lang.Number
        System.out.println(wTypeA);
    }
}

Generic

在没有泛型之前,从集合中读取每一个元素都需要进行转换,而且插入的时候也没有任何限制,很容易就插入了类型不一致的元素到同一个集合中。有了泛型后,编译器就能在插入时候进行转化,并且给出错误的提示。

对于List,套用上面的定义,ParameterizedType是由一个原生态类型(row type,Class)List,并且具有一个或多个类型参数(type parameter,TypeVariable)T构成的。

原生态类型List与List由什么区别?

其实泛型解决的一个主要问题就是类型检查,这两个区别也一样,对于一个List类型的list,能够赋值给List类型的变量,但不能赋值给List类型的变量,List是List的一个子类型,而不是参数化类型List的子类型。

    public static void main(String[] args) throws Exception {
        String s = "";
        System.out.println(s instanceof Object); // true
        List<String> strList = new ArrayList<>();
        List<Object> objList = new ArrayList<>();
        System.out.println(strList instanceof List); // true
        List rawList = strList;
        objList = strList;  // 编译报错,编译器会进行类型检查
        System.out.println(strList instanceof List<Object>); // 编译报错
    }

System.out.println(strList instanceof List); 为什么会编译报错?因为泛型信息在运行的时候是会被擦除的(虚拟机跑的时候是不知道泛型这东西的,只是编译器的手段),所以instanceof List与instance of List或LIst<?>在运行时候是没有区别的,为了类型安全,不引起开发者误解,直接就编译报错。

无限制通配符List<?> 与List有什么区别?

首先,由于上面说的List是无法赋值给List的,那如果真的有这些场景比如比较两个任意类型的List的size是否相等该如何写呢?者List<?>能解决,List是能够赋值给List<?>的变量的。

那List<?>与List还有什么区别?还是类型检查上,不能将任何元素(null除外)放到List<?>中,这样就保证了类型的安全。

那么List这种原生类型的对象还有什么用?这原生类型的对象使用如List list这样的,只是为了向前兼容。

列表优于数组

这是Effective Java上的条目,有两个原因:

    public static void main(String[] args) throws Exception {
        Object[] objects = new Object[1];
        String[] strings = new String[1];
        List<Object> objectList = new ArrayList<>(); 
        System.out.println(Object.class); // class java.lang.Object
        System.out.println(objectList.getClass()); // class java.util.ArrayList
        System.out.println(objects.getClass()); // class [Ljava.lang.Object
        System.out.println(strings instanceof Object[]); // true
    }

List的类型中没有泛型的参数的信息,class类型与泛型的类型是并列的都是一种type,而在赋值的时候,只会检查class的类型。

由上面两点,可以推出,不能有泛型数组。因为通过数组这协变的特性,能够把不同类型的泛型进行赋值。书中作者说的“数组是由缺陷的”,代码如下,和使用原始类型的List,没有泛型的容器一样,一个数组中有多种不同的类型的对象。

    public static void main(String[] args) throws Exception {
        Object[] objects = new Object[2];
        String[] strings = new String[1];
        Integer[] integers = new Integer[1];
        objects[0] = strings[0];
        objects[1] = integers[1];
    }

Spirng Type

从上面的Demo中看到,一些类型的操作还是比较麻烦的,Spring中提供了一些相关的工具类,如GenericTypeResolver,并且对于Java的类型进行了一些封装,很多源码中有ResolvableType这类型的使用。本质上只是个封装的,使用比较容易的类,并没有对Type进行功能扩展,doc上的demo:

   private HashMap<Integer, List<String>> myMap;
  
   public void example() {
       ResolvableType t = ResolvableType.forField(getClass().getDeclaredField("myMap"));
       t.getSuperType(); // AbstractMap<Integer, List<String>>
       t.asMap(); // Map<Integer, List<String>>
       t.getGeneric(0).resolve(); // Integer
       t.getGeneric(1).resolve(); // List
       t.getGeneric(1); // List<String>
       t.resolveGeneric(1, 0); // String
   }

参考

Java中的Type详解

Table of Contents