【小家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:获取到泛型类型,返回一个数组(因为可能会有多个)

相关文章
|
6月前
|
安全 Java
Java之泛型使用教程
Java之泛型使用教程
425 10
|
8月前
|
安全 Java API
在Java中识别泛型信息
以上步骤和示例代码展示了怎样在Java中获取泛型类、泛型方法和泛型字段的类型参数信息。这些方法利用Java的反射API来绕过类型擦除的限制并访问运行时的类型信息。这对于在运行时进行类型安全的操作是很有帮助的,比如在创建类型安全的集合或者其他复杂数据结构时处理泛型。注意,过度使用反射可能会导致代码难以理解和维护,因此应该在确有必要时才使用反射来获取泛型信息。
285 11
|
12月前
|
Java Linux 定位技术
Minecraft配置文件参数说明(JAVA服务器篇)
Minecraft JAVA版服务器启动后会生成server.properties配置文件,位于minecraft_server/根目录下。该文件包含多项关键设置,如游戏模式(gamemode)、最大玩家数(max-players)、难度(difficulty)等。此文档详细说明了各配置项的功能与默认值,帮助用户高效管理服务器环境。
3061 60
|
12月前
|
安全 IDE Java
重学Java基础篇—Java泛型深度使用指南
本内容系统介绍了Java泛型的核心价值、用法及高级技巧。首先阐述了泛型在**类型安全**与**代码复用**中的平衡作用,解决强制类型转换错误等问题。接着详细讲解了泛型类定义、方法实现、类型参数约束(如边界限定和多重边界)、通配符应用(PECS原则)以及类型擦除的应对策略。此外,还展示了泛型在通用DAO接口、事件总线等实际场景的应用,并总结了命名规范、边界控制等最佳实践。最后探讨了扩展知识,如通过反射获取泛型参数类型。合理运用泛型可大幅提升代码健壮性和可维护性,建议结合IDE工具和单元测试优化使用。
524 1
|
11月前
|
Java
java中一个接口A,以及一个实现它的类B,一个A类型的引用对象作为一个方法的参数,这个参数的类型可以是B的类型吗?
本文探讨了面向对象编程中接口与实现类的关系,以及里氏替换原则(LSP)的应用。通过示例代码展示了如何利用多态性将实现类的对象传递给接口类型的参数,满足LSP的要求。LSP确保子类能无缝替换父类或接口,不改变程序行为。接口定义了行为规范,实现类遵循此规范,从而保证了多态性和代码的可维护性。总结来说,接口与实现类的关系天然符合LSP,体现了多态性的核心思想。
429 0
|
Java
实现java执行kettle并传参数
实现java执行kettle并传参数
320 1
|
Java 语音技术 容器
java数据结构泛型
java数据结构泛型
162 5
|
存储 安全 Java
🌱Java零基础 - 泛型详解
【10月更文挑战第7天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
201 1
|
存储 Java 编译器
Java集合定义其泛型
Java集合定义其泛型
137 1
|
存储 Java 编译器
【用Java学习数据结构系列】初识泛型
【用Java学习数据结构系列】初识泛型
138 2