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

2021-11-09 10:23:13 +08:00
 7911364440
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());
    }

}
5062 次点击
所在节点    Java
35 条回复
xingda920813
2021-11-09 17:17:34 +08:00
对 3 楼思路的一个具体实现, 支持嵌套泛型. https://gist.github.com/xingda920813/6d01c009242b168796aa318d287a7f58
wjploop
2021-11-09 20:46:28 +08:00
@guyuesh2 感谢,又涨知识了,附上一篇查到的博文。https://www.cxyzjd.com/article/sai_simon/98663284

另外,我能不能这么理解?泛型擦除只是为了兼容以前无泛型的代码,运行时不去检验类型,而泛型信息还是保留着。
humpy
2021-11-09 21:11:08 +08:00
我之前尝试过

/**
* 获取集合的元素类型
*
* @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
2021-11-09 21:34:59 +08:00
@wjploop #22 擦除了就是没了,楼上那些实现方法是自己加了个成员标记类型
john6lq
2021-11-09 21:38:21 +08:00
三楼方法是可以,安卓封装 viewbinding 了解到的。
sagaxu
2021-11-09 21:40:54 +08:00
最好不要拿,拿了就跟调用方耦合了
v2lf
2021-11-09 23:10:29 +08:00
补充下, 基本原理实现, 使用的话,最好按照需求,封装层级继承,并且需要添加其他逻辑,处理边界情况

```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
2021-11-09 23:12:16 +08:00
@wjploop 我理解哈,raw class 是没有类型信息的,ParameterizedType 的类型信息实现,是通过读取文本解析出来的, 可以看下源码哈,jdk 中的
xarthur
2021-11-10 00:11:04 +08:00
这个就是类型擦除啊……
wjploop
2021-11-10 10:33:34 +08:00
@v2lf 你提到的文本应该指的是编译后的字节码,而非*.java 源文件吧,不太明白提到的
“raw class 是没有类型信息的,ParameterizedType 的类型信息实现”。

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

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

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

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

最重要的是,我很久没看这一块,这些只是我很早以前看得一个大概记忆和理解,可能有错误的地方,请批评指正
dbpe
2021-11-10 17:19:48 +08:00
我在想一个问题...如果 aot 和类似 native image 的推广..那些反射有什么办法解决呢?
Aresxue
2021-11-17 13:30:35 +08:00
泛型擦除会擦除到类的上界,普通的对象的上界就是 Object ,一般是拿不到的,如果 method 里有声明可以依赖 ParameterizedType 去获取,不然就取里面的对象的类型,如果还是空的那 jvm 就感知不到了,需要你手动设一个上界或者传进去

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/814075

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX