图片来自必应

    https://juejin.cn/post/6844903597977632776

    在Java中,泛型与反射是两个重要的概念,我们几乎能够经常的使用到它们。而谈起Type,如果有人还比较陌生的话 ,那么说起一个它的直接实现类——Class的话,大家都应该明白了。Type是Java语言中所有类型的公共父接口。而这篇文章,主要是讲述了Type的其它四个子类——ParameterizedType、 TypeVariable、GenericArrayType、WildcardType。对想了解这几个类的朋友一个参考

    Java中的Type类型详解 - 图1

    • ParameterizedType:参数化类型
      参数化类型即我们通常所说的泛型类型,一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。那么我们的ParameterizedType就是这样一个类型,下面我们来看看它的三个重要的方法:
      • getRawType(): Type
        该方法的作用是返回当前的ParameterizedType的类型。如一个List,返回的是List的Type,即返回当前参数化类型本身的Type。
      • getOwnerType(): Type
        返回ParameterizedType类型所在的类的Type。如Map.Entry这个参数化类型返回的事Map(因为Map.Entry这个类型所在的类是Map)的类型。
      • getActualTypeArguments(): Type[]
        该方法返回参数化类型<>中的实际参数类型, 如 Map map 这个 ParameterizedType 返回的是 String 类,Person 类的全限定类名的 Type Array。注意: 该方法只返回最外层的<>中的类型,无论该<>内有多少个<>。


    下面让我们用一段例子来看一下具体的用法:

    1. //是ParameterizedType
    2. private HashMap<String, Object> map;
    3. private HashSet set;
    4. private List list;
    5. private Class<?> clz;
    6. //不是ParameterizedType
    7. private Integer i;
    8. private String str;
    9. private static void printParameterizedType(){
    10. Field[] fields = TestParameterizedTypeBean.class.getDeclaredFields();
    11. for (Field f : fields){
    12. //打印是否是ParameterizedType类型
    13. System.out.println("FieldName: " + f.getName() + " instanceof ParameterizedType is : " +
    14. (f.getGenericType() instanceof ParameterizedType));
    15. }
    16. //取map这个类型中的实际参数类型的数组
    17. getParameterizedTypeWithName("map");
    18. getParameterizedTypeWithName("str");
    19. }
    20. private static void getParameterizedTypeWithName(String name){
    21. Field f;
    22. try {
    23. //利用反射得到TestParameterizedTypeBean类中的所有变量
    24. f = TestParameterizedTypeBean.class.getDeclaredField(name);
    25. f.setAccessible(true);
    26. Type type = f.getGenericType();
    27. if (type instanceof ParameterizedType){
    28. for(Type param : ((ParameterizedType)type).getActualTypeArguments()){
    29. //打印实际参数类型
    30. System.out.println("---type actualType---" + param.toString());
    31. }
    32. //打印所在的父类的类型
    33. System.out.println("---type ownerType0---"+ ((ParameterizedType) type).getOwnerType());
    34. //打印其本身的类型
    35. System.out.println("---type rawType---"+ ((ParameterizedType) type).getRawType());
    36. }
    37. } catch (NoSuchFieldException e) {
    38. e.printStackTrace();
    39. }
    40. }

    复制代码

    1. 上面的代码主要是定义了一些变量,这些变量中间有ParameterizedType也有普通类型变量,我们来看一下上述代码的输出:
    2. ![](//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4171b8d8ceb74b638ebab57e882ebf8b~tplv-k3u1fbpfcp-watermark.awebp)
    3. * **TypeVariable:类型变量**
    4. 范型信息在编译时会被转换为一个特定的类型, TypeVariable就是用来反映在JVM编译该泛型前的信息。\(通俗的来说,TypeVariable就是我们常用的TK这种泛型变量\)
    5. * **getBounds\(\): Type\[\]:**
    6. 返回当前类型的上边界,如果没有指定上边界,则默认为Object
    7. * **getName\(\): String:**
    8. 返回当前类型的类名
    9. * **getGenericDeclaration\(\): D**
    10. 返回当前类型所在的类的Type
    11. 下面通过一个例子来加深了解:
    12. ```java
    13. public class TestTypeVariableBean<K extends Number, T> {
    14. //K有指定了上边界Number
    15. K key;
    16. //T没有指定上边界,其默认上边界为Object
    17. T value;
    18. public static void main(String[] args){
    19. Type[] types = TestTypeVariableBean.class.getTypeParameters();
    20. for (Type type : types){
    21. TypeVariable t = (TypeVariable) type;
    22. int index = t.getBounds().length - 1;
    23. //输出上边界
    24. System.out.println("--getBounds()-- " + t.getBounds()[index]);
    25. //输出名称
    26. System.out.println("--getName()--" + t.getName());
    27. //输出所在的类的类型
    28. System.out.println("--getGenericDeclaration()--" + t.getGenericDeclaration());
    29. }
    30. }
    31. }
    32. 复制代码

    再来看下输出:

    Java中的Type类型详解 - 图2

    • GenericArrayType:泛型数组类型:
      组成数组的元素中有泛型则实现了该接口; 它的组成元素是 ParameterizedType 或 TypeVariable 类型。(通俗来说,就是由参数类型组成的数组。如果仅仅是参数化类型,则不能称为泛型数组,而是参数化类型)。注意:无论从左向右有几个[]并列,这个方法仅仅脱去最右边的[]之后剩下的内容就作为这个方法的返回值。
      • getGenericComponentType(): Type:


    返回组成泛型数组的实际参数化类型,如List[] 则返回 List。
    下面还是通过一个例子来深入了解:

    1. public class TestGenericArrayTypeBean<T> {
    2. //泛型数组类型
    3. private T[] value;
    4. private List<String>[] list;
    5. //不是泛型数组类型
    6. private List<String> singleList;
    7. private T singleValue;
    8. public static void main(String[] args){
    9. Field[] fields = TestGenericArrayTypeBean.class.getDeclaredFields();
    10. for (Field field: fields){
    11. field.setAccessible(true);
    12. //输出当前变量是否为GenericArrayType类型
    13. System.out.println("Field: "
    14. + field.getName()
    15. + "; instanceof GenericArrayType"
    16. + ": "
    17. + (field.getGenericType() instanceof GenericArrayType));
    18. if (field.getGenericType() instanceof GenericArrayType){
    19. //如果是GenericArrayType,则输出当前泛型类型
    20. System.out.println("Field: "
    21. + field.getName()
    22. + "; getGenericComponentType()"
    23. + ": "
    24. + (((GenericArrayType) field.getGenericType()).getGenericComponentType()));
    25. }
    26. }
    27. }
    28. }
    29. 复制代码


    接下来看下输出:
    Java中的Type类型详解 - 图3

    • WildcardType: 通配符类型
      表示通配符类型,比如 <?>, <? Extends Number>等
      • getLowerBounds(): Type[]: 得到下边界的数组
      • getUpperBounds(): Type[]: 得到上边界的type数组


    注:如果没有指定上边界,则默认为Object,如果没有指定下边界,则默认为String
    下面还是通过一个例子了解一下:

    1. public class TestWildcardType {
    2. public static void main(String[] args){
    3. //获取TestWildcardType类的所有方法(本例中即 testWildcardType 方法)
    4. Method[] methods = TestWildcardType.class.getDeclaredMethods();
    5. for (Method method: methods){
    6. //获取方法的所有参数类型
    7. Type[] types = method.getGenericParameterTypes();
    8. for (Type paramsType: types){
    9. System.out.println("type: " + paramsType.toString());
    10. //如果不是参数化类型则直接continue,执行下一个循环条件
    11. if (!(paramsType instanceof ParameterizedType)){
    12. continue;
    13. }
    14. //将当前类型强转为参数化类型并获取其实际参数类型(即含有通配符的泛型类型)
    15. Type type = ((ParameterizedType) paramsType).getActualTypeArguments()[0];
    16. //输出其是否为通配符类型
    17. System.out.println("type instanceof WildcardType : " +
    18. ( type instanceof WildcardType));
    19. if (type instanceof WildcardType){
    20. int lowIndex = ((WildcardType) type).getLowerBounds().length - 1;
    21. int upperIndex = ((WildcardType) type).getUpperBounds().length - 1;
    22. //输出上边界与下边界
    23. System.out.println("getLowerBounds(): "
    24. +
    25. (lowIndex >= 0 ? ((WildcardType) type).getLowerBounds()[lowIndex] : "String ")
    26. + "; getUpperBounds(): "
    27. +
    28. (upperIndex >=0 ? ((WildcardType) type).getUpperBounds()[upperIndex]:"Object"));
    29. }
    30. }
    31. }
    32. }
    33. public void testWildcardType(List<? extends OutputStream> numberList,
    34. List<? super InputStream> upperList, List<Integer> list, InputStream inputStream){}
    35. }
    36. 复制代码


    输出:
    Java中的Type类型详解 - 图4

    • 泛型的擦除的原因以及Java中Type的作用
      其实在jdk1.5之前Java中只有原始类型而没有泛型类型,而在JDK 1.5 之后引入泛型,但是这种泛型仅仅存在于编译阶段,当在JVM运行的过程中,与泛型相关的信息将会被擦除,如List与List都将会在运行时被擦除成为List这个类型。而类型擦除机制存在的原因正是因为如果在运行时存在泛型,那么将要修改JVM指令集,这是非常致命的。
      此外,原始类型在会生成字节码文件对象,而泛型类型相关的类型并不会生成与其相对应的字节码文件(因为泛型类型将会被擦除),因此,无法将泛型相关的新类型与class相统一。因此,为了程序的扩展性以及为了开发需要去反射操作这些类型,就引入了Type这个类型,并且新增了ParameterizedType, TypeVariable, GenericArrayType, WildcardType四个表示泛型相关的类型,再加上Class,这样就可以用Type类型的参数来接受以上五种子类的实参或者返回值类型就是Type类型的参数。统一了与泛型有关的类型和原始类型Class。而且这样一来,我们也可以通过反射获取泛型类型参数。

    最后,顺便打个广告,猿辅导研发招聘各岗位,有大量HC。感兴趣的话可以简历发我的邮箱哦:fb0122@163.com。欢迎各位大佬来撩~ 😜 😜