美文网首页
Java 运行时如何获取泛型参数的类型

Java 运行时如何获取泛型参数的类型

作者: zxbyh | 来源:发表于2023-08-24 16:59 被阅读0次

原文链接:https://blog.csdn.net/hj7jay/article/details/54889717

在 Java 中对于下面最简单的泛型类

class A<T> {
    public void foo() {
        //如何在此处获得运行时 T 的具体类型呢?
    }
}

设想我们使用时

new A<String>().foo();

是否能在 foo() 方法中获得当前的类型是 String 呢?答案是否定的,不能。在 foo() 方法中 this 引用给不出类型信息, this.getClass() 就更不可能了,因为 Java 的泛型不等同于 C++ 的模板类, this.getClass() 实例例是被所有的不同具体类型的 A 实例(new A<String>(), new A<Integer>() 等) 共享的,所以在字节码中类型会被擦除到上限。

了解了 Java 泛型机制是如何擦除类型的,我们接下来的问题就是如何通过反射获得泛型签名中的类型,一般会在继承或实现泛型接口时会用到它。

继承一个泛型基类

class A<T, ID> {
}
 
class B extends A<String, Integer> {
}
 
public class Generic {
 
    public static void main(String[] args) {
        System.out.println(B.class.getGenericSuperclass());
    }
}

上面的代码输出是

cc.unmi.A<java.lang.String, java.lang.Integer>

所以要获得这两个类型是可行的

B.class.getGenericSuperclass() 得到的实际类型是 ParameterizedTypeImpl 通过它就可以获得 actualTypeArguments 了。代码就是

ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericSuperclass();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for(Type actualTypeArgument: actualTypeArguments) {
    System.out.println(actualTypeArgument);
}

上面的代码输出

class java.lang.String

class java.lang.Integer

实现一个泛型接口

这时与继承一个泛型基类的情况略有不同,如下关系,A 是一个泛型接口

interface A<T, ID> {
}
 
class B implements A<String, Integer> {
}

该如何反射获得 B 的参数类型呢,用上面的方法已不可行, B.class.getGenericSuperclass() 已不是一个 ParameterizedTypeImpl 而是一个 Object 类型。现在需要另一个方法 getGenericInterfaces(): Type[] 它得到一个 Type 数组,代表类实现的多个接口,因为我们这儿只实现了一个接口,所以取第一个元素,它的类型是我们已见过的 ParameterizedTypeImpl ,

因此我们用来获得实现接口而来的泛型参数的代码就是

ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericInterfaces()[0];
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
    System.out.println(actualTypeArgument);
}

同样能得到上面的一样的结果。

继承一个泛型接口

这时与继承一个泛型基类的情况略有不同,如下关系,A 是一个泛型接口

interface A<T, ID> {
}
 
interface B extendA<String, Integer> {
}

也可以用

B.class.getGenericInterfaces();

总结一下
如果是继承基类而来的泛型,就用 getGenericSuperclass() , 转型为 ParameterizedType 来获得实际类型
如果是实现接口而来的泛型,就用 getGenericInterfaces() , 针对其中的元素转型为 ParameterizedType 来获得实际类型
我们所说的 Java 泛型在字节码中会被擦除,并不总是擦除为 Object 类型,而是擦除到上限类型
能否获得想要的类型可以在 IDE 中,或用 javap -v <your_class> 来查看泛型签名来找到线索

然后还有一种情况,多重继承

package com.rapid.demo.mybaits;

import cn.hutool.core.util.TypeUtil;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * @Description:
 * @Author zbh
 * @Date 2023/8/25
 */
public class TypeTest {
    interface A<T1, T2> {
    }

    interface B extends A<String, Integer> {
    }

    class C implements B{
    }

    public static void bGetA(){
        ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericInterfaces()[0];
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        for (Type actualTypeArgument : actualTypeArguments) {
            System.out.println(actualTypeArgument);
        }

    }

    public static void bGetAEasy(){

        for (Type actualTypeArgument :TypeUtil.toParameterizedType(B.class).getActualTypeArguments()){
            System.out.println(actualTypeArgument);
        }
    }

    public static void cGetA(){
        Type[] genericInterfaces = C.class.getGenericInterfaces();
        if (genericInterfaces.length > 0) {
            Type genericType = genericInterfaces[0];
            ParameterizedType parameterizedType = (ParameterizedType) ((Class)genericType).getGenericInterfaces()[0];
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }

    public static void cGetAEasy(){
        for (Type actualTypeArgument :TypeUtil.toParameterizedType(C.class.getGenericInterfaces()[0]).getActualTypeArguments()){
            System.out.println(actualTypeArgument);
        }
    }

    public static void main(String[] args) {
        bGetA();
        cGetA();

        bGetAEasy();
        cGetAEasy();
    }
}


这儿需要再向上找一次!

相关文章

  • Java如何在运行时获取泛型的类型

    Java泛型是伪泛型,会在编译完成时进行类型的擦除,我们无法在运行时获取泛型参数的具体类型(类型擦除会被替换成泛型...

  • Java泛型的原始类型

    众所周知,Java中的泛型在编译期被擦除,那有没有办法在运行时获取到泛型的原始类型呢?有的。 获取泛型类型 如果定...

  • ★13.泛型

    关于泛型 静态方法无法访问泛型的类型参数。(C++可以) Java泛型使用擦除实现,如运行时,List ,List...

  • Java泛型教程

    Java泛型教程导航 Java 泛型概述 Java泛型环境设置 Java泛型通用类 Java泛型类型参数命名约定 ...

  • java泛型

    java泛型 Java 泛型的参数只可以代表类,不能代表个别对象。 由于 Java 泛型的类型参数之实际类型在编译...

  • java泛型中类型擦除的一些思考

    java泛型 java泛型介绍 java泛型的参数只可以代表类,不能代表个别对象。由于java泛型的类型参数之实际...

  • JAVA 核心笔记 || [xxx] 泛型

    泛型 JAVA 的参数化类型 称为 泛型 泛型类的设计 Learn12.java 运行

  • java 泛型

    1,如何实例化泛型 2,如何获取java中的泛型类型 调用(通常在构造方法中调用):

  • 泛型

    JAVA 如何实现泛型(参数化的类型) 为什么需要泛型 1、多种数据类型执行相同的代码image.png 2、泛型...

  • 参数化泛型getGenericSuperclass

    问题:如何获取泛型参数的实际类型?也就是class对象? 泛型参数因为泛型的不确定性,我们在写代码的时候,无法直接...

网友评论

      本文标题:Java 运行时如何获取泛型参数的类型

      本文链接:https://www.haomeiwen.com/subject/gozmmdtx.html