V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
7911364440
V2EX  ›  Java

如何通过反射获取 List<T>中泛型 T 的真实类型?

  •  
  •   7911364440 · 77 天前 · 3544 次点击
    这是一个创建于 77 天前的主题,其中的信息可能已经有所发展或是发生改变。
    public class PageResult<T> {
    
        private int total;
        private List<T> data;
    
        public static <T> PageResult<T> of(Page<T> page) {
            return new PageResult<>(page.getSize(), page.getContent());
        }
    
    }
    
    35 条回复    2021-11-17 13:30:35 +08:00
    zongren
        1
    zongren  
       77 天前
    好像获取不到吧,只能把 class 传进来
    chendy
        2
    chendy  
       77 天前
    看你的需求
    如果只是想看看 List 里面是啥,取第一个元素看一眼类型就行了(对空 List 无效)
    v2lf
        3
    v2lf  
       77 天前
    通过继承, 设置一个抽象的超类 ,实现这个需求
    wolfie
        4
    wolfie  
       77 天前
    泛型是编译期,反射是运行时。
    orangie
        5
    orangie  
       77 天前
    话说,为什么当初添加泛型功能的时候,没有给添加一个字段用于保存编译时能确定的这个泛型类型呢?比如给加一个 getGenericClass()方法,编译器实现内部代码让他返回 T 的真实类型。虽然这么做好像不能用于协变和逆变,不过再加一个方法用来返回协变、逆变、准确泛型?
    kiotech
        6
    kiotech  
       77 天前
    泛型 T 可以为 interface ,即 PageResult<PageInterface>,这样就可以拿到 page.getSize(), page.getContent()
    youkiiiiiiiiiiiI
        7
    youkiiiiiiiiiiiI  
       77 天前
    GG
    leeg810312
        8
    leeg810312  
       77 天前 via Android
    @orangie JAVA 历史遗留问题,很难改了,你说的特性只有运行时真泛型才能做到,反射是运行时,编译时泛型是做不到的
    clf
        9
    clf  
       77 天前   ❤️ 1
    SpringBoot 下泛型接口:
    public interface IService<T> {
    ...
    default Class<T> getClazz() {
    return (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), IService.class);
    }
    ...
    }
    clf
        10
    clf  
       77 天前
    @clf #9 能拿到的是 public class XXXService extends IService<XXX> {} 的 XXX.class
    ZoteTheMighty
        11
    ZoteTheMighty  
       77 天前
    T 里面的类继承一个抽象类,抽象类里放一个抽象方法 getClassInfo , 然后子类实现该接口。
    guyuesh2
        12
    guyuesh2  
       77 天前   ❤️ 2
    Class#getDeclaredField

    Field#getGenericType

    ParameterizedType 和 Type 的一些子类了解一下,泛型反射是 Gson FastJson 的基础吧 !
    0xZhangKe
        13
    0xZhangKe  
       77 天前
    可以看看 Gson 是怎么做的,就是上面说的继承会保存范型类型。
    passerbytiny
        14
    passerbytiny  
       77 天前 via Android
    首先给答案,不能。原因:Java 泛型仅在编译时有效,编译之后任何泛型信息都被擦除了。
    passerbytiny
        15
    passerbytiny  
       77 天前 via Android
    当然有折衷的方法。继承关系中会残留一些泛型信息到运行时,可以用来变相获取 T 。你不能在运行时获取 List<T>中 T 的实际类型,但是可以获取 List<T extend SomeAbstractClass/Interface>中 T 的实际类型。
    passerbytiny
        16
    passerbytiny  
       77 天前 via Android
    上面说错了一点,是可以获取 List<T extend SomeAbstractClass/Interface<T>>中 T 的实际类型。
    INCerry
        17
    INCerry  
       77 天前   ❤️ 2
    如果是 C#的话 直接即可
    public Type GetListType<T>(List<T> list)
    {
    return list.GetType().GetGenericArguments()[0];
    }
    thetbw
        18
    thetbw  
       77 天前
    无解,我用的就是楼上的方法,当用 aop 对参数处理的时候,遇到 list 就把 list 中的数据取出来判断类型,如果没数据,跳过
    geligaoli
        19
    geligaoli  
       77 天前
    可以有变通的方式,

    1. 在 PageResult<T> 的子类中获取。2. 直接 new 对象时传递 T 的类型。

    public class PageResult<T> {
    private Class<T> entityClass;

    /**
    * 子类中获取 entityClass
    */
    public PageResult() {
    this.entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    /**
    * new 对象的时候传递
    *
    * @param entityClass
    */
    public PageResult(Class<T> entityClass) {
    this.entityClass = entityClass;
    }
    Edsie
        20
    Edsie  
       77 天前
    3 楼正解
    xingda920813
        21
    xingda920813  
       77 天前
    对 3 楼思路的一个具体实现, 支持嵌套泛型.
    wjploop
        22
    wjploop  
       77 天前
    @guyuesh2 感谢,又涨知识了,附上一篇查到的博文。https://www.cxyzjd.com/article/sai_simon/98663284

    另外,我能不能这么理解?泛型擦除只是为了兼容以前无泛型的代码,运行时不去检验类型,而泛型信息还是保留着。
    humpy
        23
    humpy  
       77 天前
    我之前尝试过

    /**
    * 获取集合的元素类型
    *
    * @param type 集合类型
    * @param genericType 集合的泛型类型信息
    */
    public static Type resolveCollectionElementType(Class<?> type, Type genericType) {
    if (genericType instanceof ParameterizedType) {
    ParameterizedType pt = (ParameterizedType)genericType;
    return pt.getActualTypeArguments()[0];
    } else {
    return type;
    }
    }
    learningman
        24
    learningman  
       77 天前
    @wjploop #22 擦除了就是没了,楼上那些实现方法是自己加了个成员标记类型
    john6lq
        25
    john6lq  
       77 天前 via iPhone
    三楼方法是可以,安卓封装 viewbinding 了解到的。
    sagaxu
        26
    sagaxu  
       77 天前
    最好不要拿,拿了就跟调用方耦合了
    v2lf
        27
    v2lf  
       77 天前
    补充下, 基本原理实现, 使用的话,最好按照需求,封装层级继承,并且需要添加其他逻辑,处理边界情况

    ```java

    static class Person extends TypeWrap<String> {

    }

    static abstract class TypeWrap<T>{
    private final Type type;

    protected TypeWrap(){
    Type genericSuperclass = getClass().getGenericSuperclass();
    Type raw = ((ParameterizedType)genericSuperclass).getActualTypeArguments()[0];
    //todo 需要处理范型作为类型参数
    type = raw;
    }

    public Type getType() {
    return type;
    }
    }

    ```
    v2lf
        28
    v2lf  
       77 天前
    @wjploop 我理解哈,raw class 是没有类型信息的,ParameterizedType 的类型信息实现,是通过读取文本解析出来的, 可以看下源码哈,jdk 中的
    xarthur
        29
    xarthur  
       77 天前 via iPhone
    这个就是类型擦除啊……
    wjploop
        30
    wjploop  
       76 天前   ❤️ 1
    @v2lf 你提到的文本应该指的是编译后的字节码,而非*.java 源文件吧,不太明白提到的
    “raw class 是没有类型信息的,ParameterizedType 的类型信息实现”。

    既然在字节码中保留着参数类型的信息,自然就有办法提取出来,提取方法涉及到了 ParameterizedType 。

    验证字节码中仍保留着参数类型可以使用 javap 查看,具体参考 https://wiyi.org/type-erasure.html

    另外,若是泛型信息在字节码不存在了,那么 Gson 没法正确转换 json 数据包,这也是如 12 楼提到的。
    scruel
        31
    scruel  
       76 天前
    v2ex 现在是国内的 stackoverflow 了吗?
    cheng6563
        32
    cheng6563  
       76 天前
    @wjploop 反正就是擦了,但没完全擦。
    guyuesh2
        33
    guyuesh2  
       76 天前   ❤️ 1
    @wjploop 可以去看一下 <<JAVA 虚拟机规范>> 这本书,泛型信息在 Class 文件中是通过签名(Class 文件的常量池中泛型类型标记的字符串)保留的. 泛型擦除我认为是运行时存储这个泛型变量的机制(即全部通过 Object 进行存储,而不是通过泛型的具体类型进行存储,如果通过 具体类型存储,那么 ArrayList 这个类会存在多少类型? ArrayList#get 之后好像会根据泛型类型,插入强制类型转换的代码,用于转换成为用户指定的 ArrayList 类型)

    额外扩展一下,泛型类型之间也存在父子关系,可以了解一下 PECS 法则和 协变逆变

    最重要的是,我很久没看这一块,这些只是我很早以前看得一个大概记忆和理解,可能有错误的地方,请批评指正
    dbpe
        34
    dbpe  
       76 天前
    我在想一个问题...如果 aot 和类似 native image 的推广..那些反射有什么办法解决呢?
    Aresxue
        35
    Aresxue  
       69 天前
    泛型擦除会擦除到类的上界,普通的对象的上界就是 Object ,一般是拿不到的,如果 method 里有声明可以依赖 ParameterizedType 去获取,不然就取里面的对象的类型,如果还是空的那 jvm 就感知不到了,需要你手动设一个上界或者传进去
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1304 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 23:34 · PVG 07:34 · LAX 15:34 · JFK 18:34
    ♥ Do have faith in what you're doing.