【小家Java】你真的了解Java泛型参数吗?细说java.lang.reflect.Type(ParameterizedType、TypeVariable、WildcardType...)(上)

简介: 【小家Java】你真的了解Java泛型参数吗?细说java.lang.reflect.Type(ParameterizedType、TypeVariable、WildcardType...)(上)

前言


咋一看标题,你可能会说。不就是泛型吗,平时都使用着呢,没什么难的吧。

感觉了解了,但是真正的深入才知道自己了解甚少!


没有泛型的时候,只有原始类型。此时,所有的原始类型都通过字节码文件类Class类进行抽象。Class类的一个具体对象就代表一个指定的原始类型

泛型出现之后,扩充了数据类型。从只有原始类型扩充了参数化类型、类型变量类型、限定符类型 、泛型数组类型。


深入了解Java对类型的管理,对我们理解序列化、反序列化的深入理解讲非常有帮助。

类型的父类为Type,它位于反射包java.lang.reflect内。由JDK1.5之后提供的,它的标准继承图谱吐下:

image.png


(从左到右)依次是:GenericArrayType(数组类型)、ParameterizedType(参数化类型)、WildcardType( 泛型表达式类型)、TypeVariable(类型变量)、Class(原始/基本类型)


  • Class(原始/基本类型,也叫raw type):不仅仅包含我们平常所指的类、枚举、数组、注解,还包括基本类型int、float等等
  • TypeVariable(类型变量):比如List<T>中的T等
  • WildcardType( 泛型表达式类型):例如List< ? extends Number>这种
  • ParameterizedType(参数化类型):就是我们平常所用到的泛型List、Map(注意和TypeVariable的区别)
  • GenericArrayType(数组类型):并不是我们工作中所使用的数组String[] 、byte[](这种都属于Class),而是带有泛型的数组,即T[] 泛型数组


Spring提供了更具统一的类型抽象:ResolvableType,但是本文只讲讲解JDK的


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


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

public interface Type {
    default String getTypeName() {
        return toString();
    }
}


下面就专门针对这些类型做一些案例以及分析~

GenericArrayType(数组类型)


泛型数组,描述的是形如:A< T>[]或T[]类型变量和原始类型


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


demo:


public class GenericArrayTypeTest<T> {
    // 这里面有各种各样的数组:各有不同 方便看测试效果
    // 含有泛型数组的才是GenericArrayType
    public void testGenericArrayType(List<String>[] pTypeArray, T[] vTypeArray, List<String> list,
                                     List<? extends Number> wildcardList, String[] strings, GenericArrayTypeTest[] test) {
    }
    public static void main(String[] args) {
        Method[] declaredMethods = GenericArrayTypeTest.class.getDeclaredMethods();
        for (Method method : declaredMethods) {
            // main方法不用处理
            if (method.getName().startsWith("main")) {
                continue;
            }
            // 开始处理该方法===打印出此方法签名
            System.out.println("declare Method:" + method); //declare Method:public void com.fsx.maintest.GenericArrayTypeTest.testGenericArrayType(java.util.List[],java.lang.Object[],java.util.List,java.lang.String[],com.fsx.maintest.GenericArrayTypeTest[])
            // 该方法能获取到该方法所有的实际的参数化类型,比如本例中有五个参数,那数组长度就是5
            Type[] types = method.getGenericParameterTypes();
            // 分组打印出来
            for (Type type : types) {
                if (type instanceof ParameterizedType) {
                    ParameterizedType parameterizedType = (ParameterizedType) type;
                    System.out.println("ParameterizedType type :" + parameterizedType);
                }
                else if (type instanceof GenericArrayType) {
                    // 从结果
                    GenericArrayType genericArrayType = (GenericArrayType) type;
                    System.out.println("GenericArrayType type :" + genericArrayType);
                    Type genericComponentType = genericArrayType.getGenericComponentType();
                    System.out.println("genericComponentType:" + genericComponentType);
                }
                else if (type instanceof WildcardType) {
                    WildcardType wildcardType = (WildcardType) type;
                    System.out.println("WildcardType type :" + wildcardType);
                }
                else if (type instanceof TypeVariable) {
                    TypeVariable typeVariable = (TypeVariable) type;
                    System.out.println("TypeVariable type :" + typeVariable);
                }
                else {
                    Class clazz = (Class) type;
                    System.out.println("type :" + clazz);
                }
            }
        }
    }
}


输出结果:


declare Method:public void com.fsx.maintest.GenericArrayTypeTest.testGenericArrayType(java.util.List[],java.lang.Object[],java.util.List,java.util.List,java.lang.String[],com.fsx.maintest.GenericArrayTypeTest[])
GenericArrayType type :java.util.List<java.lang.String>[]
genericComponentType:java.util.List<java.lang.String>
GenericArrayType type :T[]
genericComponentType:T
ParameterizedType type :java.util.List<java.lang.String>
ParameterizedType type :java.util.List<? extends java.lang.Number>
type :class [Ljava.lang.String;
type :class [Lcom.fsx.maintest.GenericArrayTypeTest;


image.png


分析:GenericArrayType有两个都是:List<String>[] pTypeArray, T[] vTypeArray它哥俩都是泛型数组。但是这两String[] strings, Main[] test可不是,他俩属于Class普通类型


genericArrayType.getGenericComponentType()返回的类型为ParameterizedTypeImpl类型,能够获取到数组的实际参数类型


ParameterizedType(参数化类型)


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

public interface ParameterizedType extends Type {
  //获取类型内部的参数化类型 比如Map<K,V>里面的K,V类型
  Type[] getActualTypeArguments();
  // 类的原始类型,一般都是Class
  Type getRawType();
  // 获取所有者类型(只有内部类才有所有者,比如Map.Entry他的所有者就是Map),若不是内部类,此处返回null
    Type getOwnerType();
}


demo:


public class ParameterizedTypeTest {
    private Map<String, ParameterizedTypeTest> map;
    private Set<String> set1;
    private Class<?> clz;
    private Holder<String> holder;
    private List<String> list;
    private ArrayList<String> arrayList; 
    private Map.Entry<String, String> entry;
    private String str;
    private Integer i;
    private Set set;
    private List aList;
    static class Holder<V> {
    }
    public static void main(String[] args) {
        Field f = null;
        try {
            // 拿到所有的字段
            Field[] fields = ParameterizedTypeTest.class.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                f = fields[i];
                if (f.getGenericType() instanceof ParameterizedType) {
                    ParameterizedType parameterizedType = (ParameterizedType) f.getGenericType();
                    System.out.println(f.getName() + ":");
                    System.out.println("\t ParameterizedType:" + Arrays.asList(parameterizedType.getActualTypeArguments()));
                    System.out.println("\t getRawType:" + parameterizedType.getRawType());
                    System.out.println("\t getOwnerType:" + parameterizedType.getOwnerType());
                }
                // 输出不是ParameterizedType 参数化类型的
                else {
                    System.out.println(f.getName() + ":is not ParameterizedType ");
                }
            }
        } catch (Exception e) {
        }
    }
}


输出:


map:
   ParameterizedType:[class java.lang.String, class com.fsx.maintest.ParameterizedTypeTest]
   getRawType:interface java.util.Map
   getOwnerType:null
set1:
   ParameterizedType:[class java.lang.String]
   getRawType:interface java.util.Set
   getOwnerType:null
clz:
   ParameterizedType:[?]
   getRawType:class java.lang.Class
   getOwnerType:null
holder:
   ParameterizedType:[class java.lang.String]
   getRawType:class com.fsx.maintest.ParameterizedTypeTest$Holder
   getOwnerType:class com.fsx.maintest.ParameterizedTypeTest
list:
   ParameterizedType:[class java.lang.String]
   getRawType:interface java.util.List
   getOwnerType:null
arrayList:
   ParameterizedType:[class java.lang.String]
   getRawType:class java.util.ArrayList
   getOwnerType:null
entry:
   ParameterizedType:[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 
aList:is not ParameterizedType 



先看最后几个is not..的。发现即使是List,但是我们没给与泛型,它不会是ParameterizedType参数化类型。

然后holder和entry的getOwnerType不是null,因为他俩类型都是内部类,所以有所有者类型。其它的top类都是null

getRawType其实就是返回了本类的本来的Class类型

getActualTypeArguments:获取到泛型类型,返回一个数组(因为可能会有多个)

相关文章
|
2月前
|
Java API
[Java]泛型
本文详细介绍了Java泛型的相关概念和使用方法,包括类型判断、继承泛型类或实现泛型接口、泛型通配符、泛型方法、泛型上下边界、静态方法中使用泛型等内容。作者通过多个示例和测试代码,深入浅出地解释了泛型的原理和应用场景,帮助读者更好地理解和掌握Java泛型的使用技巧。文章还探讨了一些常见的疑惑和误区,如泛型擦除和基本数据类型数组的使用限制。最后,作者强调了泛型在实际开发中的重要性和应用价值。
25 0
[Java]泛型
|
14天前
|
Java
在Java中定义一个不做事且没有参数的构造方法的作用
Java程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误,因为Java程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。
|
2月前
|
存储 安全 Java
🌱Java零基础 - 泛型详解
【10月更文挑战第7天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
15 1
|
2月前
|
Java 语音技术 容器
java数据结构泛型
java数据结构泛型
27 5
|
2月前
|
存储 Java 编译器
Java集合定义其泛型
Java集合定义其泛型
19 1
|
3月前
|
Java 编译器 容器
Java——包装类和泛型
包装类是Java中一种特殊类,用于将基本数据类型(如 `int`、`double`、`char` 等)封装成对象。这样做可以利用对象的特性和方法。Java 提供了八种基本数据类型的包装类:`Integer` (`int`)、`Double` (`double`)、`Byte` (`byte`)、`Short` (`short`)、`Long` (`long`)、`Float` (`float`)、`Character` (`char`) 和 `Boolean` (`boolean`)。包装类可以通过 `valueOf()` 方法或自动装箱/拆箱机制创建。
39 9
Java——包装类和泛型
|
2月前
|
存储 算法 Java
java制作海报六:Graphics2D的RenderingHints方法参数详解,包括解决文字不清晰,抗锯齿问题
这篇文章是关于如何在Java中使用Graphics2D的RenderingHints方法来提高海报制作的图像质量和文字清晰度,包括抗锯齿和解决文字不清晰问题的技术详解。
57 0
java制作海报六:Graphics2D的RenderingHints方法参数详解,包括解决文字不清晰,抗锯齿问题
|
2月前
|
存储 Java 编译器
【用Java学习数据结构系列】初识泛型
【用Java学习数据结构系列】初识泛型
20 2
|
2月前
|
Java
java构造方法时对象初始化,实例化,参数赋值
java构造方法时对象初始化,实例化,参数赋值
55 1
|
2月前
|
安全 Java 编译器
Java基础-泛型机制
Java基础-泛型机制
16 0