Java中获取泛型类型信息的方法

 更新时间:2023年03月08日 09:56:18   作者:骑个小蜗牛  
本文主要介绍了Java中获取泛型类型信息的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

根据使用泛型位置的不同可以分为:声明侧泛型、使用侧泛型。

声明侧的泛型信息被记录在Class文件的Constant pool中以Signature的形式保存。而使用侧的泛型信息并没有保存。

声明侧泛型

声明侧泛型包括:

  • 泛型类,或泛型接口的声明
  • 带有泛型参数的成员变量
  • 带有泛型参数的方法

使用侧泛型

使用侧泛型包括:

  • 方法的局部变量,
  • 方法调用时传入的变量

获取泛型类型相关方法

上文有提到,声明侧的泛型被记录在Class文件的Constant pool中以Signature的形式保存。

JDK的Class、Field、Method类提供了一系列的获取泛型类型的相关方法。

1. Class类的泛型方法

Type getGenericSuperclass():获取父类的Type

  • 若父类有泛型,返回的实际Type是ParameterizedType接口的实现类ParameterizedTypeImpl类
  • 若父类无泛型,返回的实际Type是Class类

Type[] getGenericInterfaces():获取父接口的Type集合

  1. 若父类有泛型,返回的实际Type是ParameterizedType接口的实现类ParameterizedTypeImpl类
  2. 若父类无泛型,返回的实际Type是Class类

2. Field类的泛型方法

Type getGenericType():获取字段的Type

  • 若字段有泛型,返回的实际Type是ParameterizedType接口的实现类ParameterizedTypeImpl类
  • 若字段无泛型,返回的实际Type是Class类

3. Method类的泛型方法

Type getGenericReturnType():获取方法返回值的Type

  • 若返回值有泛型,返回的实际Type是ParameterizedType接口的实现类ParameterizedTypeImpl类
  • 若返回值无泛型,返回的实际Type是Class类

Type[] getGenericParameterTypes():获取方法参数的Type集合

  • 若方法参数有泛型,返回的实际Type是ParameterizedType接口的实现类ParameterizedTypeImpl类
  • 若方法参数无泛型,返回的实际Type是Class类

Type[] getGenericExceptionTypes():获取方法声明的异常的Type集合

  • 若方法参数有泛型,返回的实际Type是ParameterizedType接口的实现类ParameterizedTypeImpl类
  • 若方法参数无泛型,返回的实际Type是Class类

4. ParameterizedType类

ParameterizedType是Type的子接口,表示参数化类型,用于获取泛型的参数类型。

ParameterizedType的主要方法:

  • Type[] getActualTypeArguments():获取实际类型参数的Type集合
  • Type getRawType():获取声明此类型的类或接口的Type
  • Type getOwnerType():如果声明此类型的类或接口为内部类,这返回的是该内部类的外部类的Type(也就是该内部类的拥有者)

获取声明侧的泛型类型信息

  • 泛型类,或泛型接口的声明
  • 带有泛型参数的成员变量
  • 带有泛型参数的方法

示例:

public class MyTest extends TestClass<String> implements TestInterface1<Integer>,TestInterface2<Long> {

    private List<Integer> list;

    private Map<Integer, String> map;

    public List<String> aa() {
        return null;
    }

    public void bb(List<Long> list) {

    }

    public static void main(String[] args) throws Exception {
        System.out.println("======================================= 泛型类声明的泛型类型 =======================================");
        ParameterizedType parameterizedType = (ParameterizedType)MyTest.class.getGenericSuperclass();
        System.out.println(parameterizedType.getTypeName() + "--------->" + parameterizedType.getActualTypeArguments()[0].getTypeName());

        Type[] types = MyTest.class.getGenericInterfaces();
        for (Type type : types) {
            ParameterizedType typ = (ParameterizedType)type;
            System.out.println(typ.getTypeName() + "--------->" + typ.getActualTypeArguments()[0].getTypeName());
        }

        System.out.println("======================================= 成员变量中的泛型类型 =======================================");
        ParameterizedType parameterizedType1 = (ParameterizedType)MyTest.class.getDeclaredField("list").getGenericType();
        System.out.println(parameterizedType1.getTypeName() + "--------->" + parameterizedType1.getActualTypeArguments()[0].getTypeName());

        ParameterizedType parameterizedType2 = (ParameterizedType)MyTest.class.getDeclaredField("map").getGenericType();
        System.out.println(parameterizedType2.getTypeName() + "--------->" + parameterizedType2.getActualTypeArguments()[0].getTypeName()+","+parameterizedType2.getActualTypeArguments()[1].getTypeName());

        System.out.println("======================================= 方法参数中的泛型类型 =======================================");
        ParameterizedType parameterizedType3 = (ParameterizedType)MyTest.class.getMethod("aa").getGenericReturnType();
        System.out.println(parameterizedType3.getTypeName() + "--------->" + parameterizedType3.getActualTypeArguments()[0].getTypeName());

        System.out.println("======================================= 方法返回值中的泛型类型 =======================================");
        Type[] types1 = MyTest.class.getMethod("bb", List.class).getGenericParameterTypes();
        for (Type type : types1) {
            ParameterizedType typ = (ParameterizedType)type;
            System.out.println(typ.getTypeName() + "--------->" + typ.getActualTypeArguments()[0].getTypeName());
        }
    }
}

class TestClass<T> {

}

interface TestInterface1<T> {

}

interface TestInterface2<T> {

}

输出

======================================= 泛型类声明的泛型类型 =======================================
com.joker.test.generic.TestClass<java.lang.String>--------->java.lang.String
com.joker.test.generic.TestInterface1<java.lang.Integer>--------->java.lang.Integer
com.joker.test.generic.TestInterface2<java.lang.Long>--------->java.lang.Long
======================================= 成员变量中的泛型类型 =======================================
java.util.List<java.lang.Integer>--------->java.lang.Integer
java.util.Map<java.lang.Integer, java.lang.String>--------->java.lang.Integer,java.lang.String
======================================= 方法参数中的泛型类型 =======================================
java.util.List<java.lang.String>--------->java.lang.String
======================================= 方法返回值中的泛型类型 =======================================
java.util.List<java.lang.Long>--------->java.lang.Long

获取使用侧的泛型类型信息

上面讲的相关类的获取泛型类型相关方法都只是针对声明侧的泛型。因为声明侧的泛型被记录在Class文件的Constant pool中以Signature的形式保存。所以Java提供了相关方法能获取到这些信息。

那使用侧的泛型信息怎么获取呢?由于使用侧的泛型信息在编译期的时候就被类型擦除了,所以运行时是没办法获取到这些泛型信息的。

难道就真的没办法了吗,其实还是有的。使用侧需要获取泛型信息的地方主要是:方法调用时传入的泛型变量,通常需要在方法中获取变量的泛型类型。比如在JSON解析(反序列化)的场景,他们是怎么实现的了。

针对获取使用侧的泛型类型信息,主要实现方案是通过匿名内部类。

Gson中的泛型抽象类TypeToken<T>,FastJson中的泛型类TypeReference<T>等就是用的该方案。

匿名内部类实现获取使用侧的泛型类型

上文有讲到,在声明侧的泛型中,针对泛型类或泛型接口的声明的泛型,Class类提供了getGenericSuperclass()、getGenericInterfaces()来获取其子类(实现类)上声明的具体泛型类型信息。

而匿名内部类是什么?其本质就是一个继承/实现了某个类(接口,普通类,抽象类)的子类匿名对象。

匿名内部类实现获取使用侧的泛型类型的原理:

  • 定义泛型类,泛型类中有一个Type类型的字段,用于保存泛型类型的Type
  • 通过匿名内部类的方式创建该泛型类的子类实例(指定了具体的泛型类型)
    在创建子类实例的构造方法中,已经通过子类的Class的getGenericSuperclass()获取到了泛型类型信息并复制给了Type类型的字段中。
  • 随后任何地方,只要得到了该子类实例,就可以通过实例得到泛型类型的Type,这就得到了使用侧的泛型类信息。

简单示例:

定义泛型类TestClass2<T>,类中包含字段Type

public abstract class TestClass2<T> {

    private final Type type;

    public TestClass2() {
        Type superClass = getClass().getGenericSuperclass();
        if (!(superClass instanceof ParameterizedType)) {
            throw new IllegalArgumentException("无泛型类型信息");
        }
        type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public Type getType() {
        return type;
    }
}

测试获取泛型类型

public class Test {

    public static  <T> T get(TestClass2<T> tTestClass2) throws IllegalAccessException, InstantiationException {
        Type type = tTestClass2.getType();
        Class clazz = (Class) type;
        return (T)clazz.newInstance();
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        String str = get(new TestClass2<String>() {});
        Date date = get(new TestClass2<Date>() {});
    }
}

到此这篇关于Java中获取泛型类型信息的方法的文章就介绍到这了,更多相关Java获取泛型类型信息内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java 客户端向服务端上传mp3文件数据的实例代码

    Java 客户端向服务端上传mp3文件数据的实例代码

    这篇文章主要介绍了Java 客户端向服务端上传mp3文件数据的实例代码,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-09-09
  • maven package后Idea项目中找不到target文件的解决

    maven package后Idea项目中找不到target文件的解决

    在Idea中执行mavenpackage打包后,target文件不显示,点击「ShowinExplore」可以在本地文件夹中查到,解决方法:在Idea的Maven工具窗口中,右键点击项目,选择Reimport,刷新项目即可
    2024-11-11
  • Spring MVC中自定义拦截器的实例讲解

    Spring MVC中自定义拦截器的实例讲解

    下面小编就为大家带来一篇Spring MVC中自定义拦截器的实例讲解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • 启动 Eclipse 弹出 Failed to load the JNI shared library jvm.dll 错误的解决方法

    启动 Eclipse 弹出 Failed to load the JNI shared library jvm.dll

    这篇文章主要介绍了有时候,新电脑上回碰到打开Eclipse时,弹出提示“Failed to load the JNI shared library jvm.dll”错误,这里给大家分享解决方案
    2016-08-08
  • Java使用反射和动态代理实现一个View注解绑定库

    Java使用反射和动态代理实现一个View注解绑定库

    这篇文章主要介绍了Java使用反射和动态代理实现一个View注解绑定库,代码简洁,使用简单,扩展性强,结合实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2022-05-05
  • Java关键字之instanceof详解

    Java关键字之instanceof详解

    instanceof是Java的一个二元操作符,和==,>,<是同一类东东。由于它是由字母组成的,所以也是Java的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据
    2021-11-11
  • Java中Lombok的@Builder注解注意事项

    Java中Lombok的@Builder注解注意事项

    这篇文章主要介绍了Java中Lombok的@Builder注解注意事项,使用Lombok也会造成很多问题,尤其@Builder 有个很大的坑,已经见过好几次由于使用@Builder注解导致默认值失效的问题,如果测试时没有在意这个问题,就很容易引发线上问题,需要的朋友可以参考下
    2023-12-12
  • Java实现评论回复功能的完整步骤

    Java实现评论回复功能的完整步骤

    这篇文章主要给大家介绍了关于Java实现评论回复功能的完整步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • java+mysql实现登录和注册功能

    java+mysql实现登录和注册功能

    这篇文章主要为大家详细介绍了java+mysql实现登录和注册功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • 全面了解servlet中cookie的使用方法

    全面了解servlet中cookie的使用方法

    下面小编就为大家带来一篇全面了解servlet中cookie的使用方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06

最新评论