概述

在JDK1.5之前只有原始类型,此时所有的原始类型都通过字节码文件类Class进行抽象。Class类的一个具体对象就代表一个指定的原始类型。

JDK1.5加入了泛型类,扩充了数据类型,从只有原始类型基础上扩充了类型变量类型、通配符类型、参数化类型、泛型数组类型。

Type是Java语言中所有类型(Class)的公共父接口。

Type类关系图

java type是什么 java中的typeof_jvm


从左到右依次是:

  • Class(原始/基本类型):不仅包括我们平常所指的类、枚举、数组、注解,还包括基本类型int、float等等。
  • TypeVariable(类型变量):比如List<T>中的T等。
  • WildcardType(通配符类型):也叫做泛型表达式类型,例如List<? extends Number>这种。
  • ParameterizedType(参数化类型):就是我们平常所用到的泛型List<T>、Map<K,V>(注意和TypeVariable的区别,参数化类型表示的是List<T>这样的一个整体而不只是T)。
  • GenericArrayType(泛型数组类型):并不是我们工作中所使用的数组String[]、Byte[](这些都是Class),而是到带有泛型的数组,即T[]

Type接口本身算是一个标记接口,不提供任何需要复写的方法。

TypeVariable类型变量

泛型信息在编译时会被转换成一个特定的类型,而TypeVariable就是用来反应JVM编辑该泛型前的信息。(通俗的讲,TypeVariable就是我们常用的List<T>、Map<K,V>中的T,K这种泛型变量)。

还可以对类型变量加上extends限定,这样会有类型变量对应的上限;值的注意的是,类型变量的上限可以有多个,必须使用&连接,例如:List<T extends Number & Serializable>,其中&后必须是接口。

1)TypeVariable源码:

public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
 	// 类型变量对应的上边界,如果没有指定上限,返回Object 可以有多个
    Type[] getBounds();
    // 获取类型变量所在类的Type,比如TypeVariableTest<T>类,getGenericDeclaration()得到的就是TypeVariableTest。
    D getGenericDeclaration();
	// 获取类型变量在源码中定义的名称
    String getName();
	// JDK8新增的,获取注解类型的上限数组
     AnnotatedType[] getAnnotatedBounds();
}

2)具体用法:

public class TypeVariableTest<K extends Number, T> {
    // K有指定上边界 Number
    private K key;
    // T没有指定上边界,其默认上边界为Object
    private T value;

    public static void main(String[] args) {
        TypeVariable<Class<TypeVariableTest>>[] typeParameters = TypeVariableTest.class.getTypeParameters();
        for (TypeVariable<Class<TypeVariableTest>> type : typeParameters) {
            int index = type.getBounds().length - 1;
            // 输出上边界
            System.out.println("---getBounds()-- " + type.getBounds()[index]);
            // 输出名称
            System.out.println("---getName()-- " + type.getName());
            // 输出所在的类的类型
            System.out.println("---getGenericDeclaration()-- " + type.getGenericDeclaration());
        }
    }
}

3)输出结果:

---getBounds()-- class java.lang.Number
---getName()-- K
---getGenericDeclaration()-- class com.study.concurrentprogramming.genericiy.TypeVariableTest
---getBounds()-- class java.lang.Object
---getName()-- T
---getGenericDeclaration()-- class com.study.concurrentprogramming.genericiy.TypeVariableTest

WildCardType通配符类型

表示通配符类型,比如<?><? extends T><? super T>

  • extends:限定通配符的上限(如果没有指定,则默认为java.lang.Object)
  • super:限定通配符的下限(无默认下限)

1)WildCardType源码:

public interface WildcardType extends Type {
 	// 获得泛型表达式上边界,表达式中使用extends
    Type[] getUpperBounds();
 	// 或者泛型表达式的下边界,表达式中使用super
    Type[] getLowerBounds();
}

2)具体用法:

public class WildcardTypeTest {
    // 使用通配符类型参数的方法
    public void testWildcardType(
    	List<? extends OutputStream> outputStreams, 
    	List<? super InputStream> inputStreams,
        List<Integer> list, InputStream inputStream) {
    }
    public static void main(String[] args) {
        // 获取WildcardTypeTest类的所有方法(本例中是testWildcardType方法)
        Method[] declaredMethods = WildcardTypeTest.class.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println("method name: " + method.getName());
            // 获取方法的所有参数类型
            Type[] genericParameterTypes = method.getGenericParameterTypes();
            for (Type type : genericParameterTypes) {
                System.out.println("type: " + type.toString());
                // 如果不是参数化类型则直接continue
                if (!(type instanceof ParameterizedType)) {
                    continue;
                }
                // 将当前类型强转为参数化类型并获取实际参数(即含有通配符的泛型类型)
                Type actualTypeArgument = ((ParameterizedType) type).getActualTypeArguments()[0];
                // 输出其是否为通配符类型
                System.out.println("type instanceof WildcardType: "
                        + (actualTypeArgument instanceof WildcardType));
                if (actualTypeArgument instanceof WildcardType) {
                    int lowIndex = ((WildcardType) actualTypeArgument).getLowerBounds().length - 1;
                    int upperIndex = ((WildcardType) actualTypeArgument).getUpperBounds().length - 1;
                    // 输出上边界与下边界
                    System.out.println("getLowerBounds(): " +
                            (lowIndex >= 0 ? ((WildcardType) actualTypeArgument).getLowerBounds()[lowIndex] : "String")
                            + ";getUpperBounds(): " +
                            (upperIndex >= 0 ? ((WildcardType) actualTypeArgument).getUpperBounds()[upperIndex] : "Object"));
                }
            }
        }
    }
}

3)输出结果:

method name: main
type: class [Ljava.lang.String;
method name: testWildcardType
type: java.util.List<? extends java.io.OutputStream>
type instanceof WildcardType: true
getLowerBounds(): String;getUpperBounds(): class java.io.OutputStream
type: java.util.List<? super java.io.InputStream>
type instanceof WildcardType: true
getLowerBounds(): class java.io.InputStream;getUpperBounds(): class java.lang.Object
type: java.util.List<java.lang.Integer>
type instanceof WildcardType: false
type: class java.io.InputStream

ParameterizedType参数化类型

参数化类型,即平常用的泛型,例如:List<T>、Map<K,V>等带有参数化的对象。

如何理解呢?一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。

那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化(比如:List<T>T就是类型参数,而List<T>就是参数化类型),类似于方法中的参数变量,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

1)ParameterizedType源码:

public interface ParameterizedType extends Type {
 	// 获取类型内部的参数化类型,比如Map<K,V>里面的K,V类型;
 	// 注意该方法只返回最外层的<>中的类型,无论该<>内有多少个<>。
    Type[] getActualTypeArguments();
 	// 类的原始类型,一般都是Class
    Type getRawType();
	// 获取所有者类型(只有内部类才有所有者,比如Map.Entry的所有者就是Map),如果不是内部类,返回null。
    Type getOwnerType();
}

2)具体用法:

@Slf4j
public class ParameterizedTypeTest {
    private Map<String, ParameterizedTypeTest> map;
    private Set<String> setStr;
    private Class<?> clz;
    private Holder<String> holder;
    private List<String> listStr;
    private ArrayList<String> arrayList;
    private Map.Entry<String, String> entry;

    // 非参数化类型
    private String str;
    private Integer i;
    private Set set;
    private List list;

    static class Holder<V> {
    }
    public static void main(String[] args) {
        try {
            // 拿到所有的字段
            Field[] fields = ParameterizedTypeTest.class.getDeclaredFields();
            Arrays.stream(fields).forEach(item -> {
                if (item.getGenericType() instanceof ParameterizedType) {
                    item.setAccessible(true);
                    ParameterizedType parameterizedType = (ParameterizedType) item.getGenericType();
                    log.info("{} :", item.getName());
                    log.info("getActualTypeArguments: {}", Arrays.asList(parameterizedType.getActualTypeArguments()));
                    log.info("getRawType: {}", parameterizedType.getRawType());
                    log.info("getOwnerType: {}", parameterizedType.getOwnerType());
                } else {
                    log.info("{} is not ParameterizedType", item.getName());
                }
            });
        } catch (Exception e) {
            log.info("exception:", e);
        }
    }
}

3)输出结果:

map :
getActualTypeArguments: [class java.lang.String, class com.study.concurrentprogramming.genericiy.ParameterizedTypeTest]
getRawType: interface java.util.Map
getOwnerType: null
setStr :
getActualTypeArguments: [class java.lang.String]
getRawType: interface java.util.Set
getOwnerType: null
clz :
getActualTypeArguments: [?]
getRawType: class java.lang.Class
getOwnerType: null
holder :
getActualTypeArguments: [class java.lang.String]
getRawType: class com.study.concurrentprogramming.genericiy.ParameterizedTypeTest$Holder
getOwnerType: class com.study.concurrentprogramming.genericiy.ParameterizedTypeTest
listStr :
getActualTypeArguments: [class java.lang.String]
getRawType: interface java.util.List
getOwnerType: null
arrayList :
getActualTypeArguments: [class java.lang.String]
getRawType: class java.util.ArrayList
getOwnerType: null
entry :
getActualTypeArguments: [class java.lang.String, class java.lang.String]
getRawType: interface java.util.Map$Entry
getOwnerType: interface java.util.Map
str is not ParameterizedType
i is not ParameterizedType
set is not ParameterizedType
list is not ParameterizedType

先看后面几个,发现即使是List,但是我们没给与泛型,它也不是ParameterizedType参数化类型,因此如果想成为参数化类型必须要指定泛型参数才行。

GenericArrayType泛型数组类型

描述的是形如:A[](参数化类型数组)或者T[](类型变量数组),它的组成元素是ParameterizedType或TypeVariable类型。

无论从左向右有几个[]并列,这个方法仅仅脱去最右边的[]之后剩下的内容就作为这个方法的返回值。

1)GenericArrayType源码:

public interface GenericArrayType extends Type {
    // 返回泛型数组中成员类型,即List<String>[]中的List<String>
    Type getGenericComponentType();
}

2)具体用法:

public class GenericArrayTypeTest<T> {
    // 泛型数组类型
    private T[] value;
    private List<String>[] lists;

    // 非泛型数组类型
    private List<String> list;
    private T singleValue;

    public static void main(String[] args) {
        Field[] declaredFields = GenericArrayTypeTest.class.getDeclaredFields();
        Arrays.stream(declaredFields).forEach(field -> {
            field.setAccessible(true);
            // 输出当前变量是否为GenericArrayType类型
            System.out.println("Field: " + field.getName() + "; instanceof GenericArrayType: "
                    + (field.getGenericType() instanceof GenericArrayType));
            if (field.getGenericType() instanceof GenericArrayType) {
                // 输出泛型类型
                System.out.println("Field: " + field.getName() + "; getGenericComponentType():"
                        + ((GenericArrayType) field.getGenericType()).getGenericComponentType());
            }
            field.setAccessible(false);
        });
    }
}

3)输出结果:

Field: value; instanceof GenericArrayType: true
Field: value; getGenericComponentType():T
Field: lists; instanceof GenericArrayType: true
Field: lists; getGenericComponentType():java.util.List<java.lang.String>
Field: list; instanceof GenericArrayType: false
Field: singleValue; instanceof GenericArrayType: false

泛型中使用&(并且)操作符

我们有时候可以看到泛型搭配上&的使用方式,比如:

// 在类上声明,约束泛型类变量
static <R extends Enum<R> & BaseIntEnum> List<R> parse2Enums(...){...}
// 方法上声明泛型
public <R extends Enum<R> & Serializable> List<R> parse2Enums(){}

需要注意的是:&只能放在泛型的声明上,&不能用于?通配符上(因为通配符不能放在泛型的声明上),&后面只能放置接口,而不能是具体类型,连Object都不行,因此当我们需要泛型的多重约束的时候,可以使用&操作符。

注意:在类级别不允许泛型中的 super 关键字,例如,如下代码编译不通过:

ConstructorTest<T super CharSequence>

满意的回答是:类型擦除后变为<Object>,Object又是所有类型的父类型,所有类型就都可以作为T,是没有意义的事情。

Class原始/基本类型

Tyep的直接子类只有一个,也就是Class,代表着类型中的原始类型以及基本类型。

Class-反射基石,其意义为类的抽象,即对类做描述,比如对类有修饰、字段、方法等属性,提供获得该类的所有方法、所有公共方法等的方法,同时,Class也是Java中最重要的一种,表示原始类型(引用类型)及基本类型。

与泛型有关的类型不能和原始类型统一到Class的原因

1)Java中如何引入泛型

为了使用泛型而不真正引入泛型,Java采用泛型擦除机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转化的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除。

2)产生泛型擦除的原因

原始类型和新产生的类型都应该统一成各自的字节码文件类型对象,但是由于泛型不是最初Java中的成分。如果真的加入了泛型,涉及到JVM指令集到的修改,这是非常致命的(简单来说就是Java要向下兼容,所以它的泛型只是语法层面的技术,对于JVM是不承认的)。

Java引入泛型擦除的原因是避免因为引入泛型而导致运行时创建不必要的类。

那我们其实就可以通过定义类的方式,在类信息中保留泛型信息,从而在运行时获得这些泛型信息;简而言之,Java的泛型擦除是有范围的,即类定义中的泛型是不会被擦除的。

3)引入Type的原因

为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType、TypeVariable、 GenericArrayType、WildcardType几种类型来代表不能被归一到Class类中的类型,但是又和原始类型齐名的类型。

为了程序的扩展性,最终引入了Type接口作为Class和ParameterizedType、TypeVariable、GenericArrayType、WildcardType这几种类型的父接口。这样可以用Type类型的参数来接受以上五种子类的实参或者返回值类型就是Type类型的参数,统一了与泛型有关的类型和原始类型Class。

4)Type接口中沒有方法的原因

从上面看到,Type的出现仅仅起到了通过多态来达到程序扩展性提高的作用,没有其他的作用。因此Type接口的源码中没有任何方法。

5)总结

我们知道,Type是JDK1.5引入的,其引入主要是为了泛型,没有泛型之前,只有所谓的原始类型。此时,所有的与原始类型都通过字节码文件类Class进行抽象。Class类的一个具体对象就代表一个指定的原始类型。

泛型出现之后,也就扩充了数据类型。从只有原始类型扩充了参数化类型、类型变量类型、通配符类型、泛型数组类型,也就是Type的子接口。

List<T ? entends>[]:这里的List<T ? entends>就是ParameterizedType,T就是TypeVariable,T ? entends就是WildcardType(注意:WildcardType不是Java类型,而是一个表达式),整个List<T ? entends>[]就是GenericArrayType。