Java中如何获取泛型类型信息
文章目录
- 声明侧泛型
- 使用侧泛型
- 获取泛型类型相关方法
- 1. Class类的泛型方法
- 2. Field类的泛型方法
- 3. Method类的泛型方法
- 4. ParameterizedType类
- 获取声明侧的泛型类型信息
- 获取使用侧的泛型类型信息
- 匿名内部类实现获取使用侧的泛型类型
根据使用泛型位置的不同可以分为:声明侧泛型、使用侧泛型。
声明侧的泛型信息被记录在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集合
- 若父类有泛型,返回的实际Type是ParameterizedType接口的实现类ParameterizedTypeImpl类
- 若父类无泛型,返回的实际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中如何获取泛型类型信息
文章目录声明侧泛型使用侧泛型获取泛型类型相关方法1. Class类的泛型方法2. Field类的泛型方法3. Method类的泛型方法4. ParameterizedType类获取声明侧的泛型类型信息获取使用侧的泛型类型信息匿名内部类实现获取使用侧的泛型类型根据使用泛型位置的不同可以分为:声…...
【云原生】centos7搭建安装k8s集群 v1.25版本详细教程实战
文章目录前言一. 实验环境二. k8s 的介绍三 . k8s的安装3.1 搭建实验环境3.1.1 硬件层面的要求3.1.2 软件层面环境配置3.2 docker的安装3.2.1 搭建docker3.2.2 部署 cri-dockerd3.3 部署k8s3.3.1 配置添加阿里云的yum源3.3.2 安装kubeadm kubelet kubectl3.3.3 k8s-master节点初…...
c语言指针
指针 指针是存放地址的变量,也可以说指针地址。 对于定义p(这里的话,只是定义,说明p是指针),p作为一个指针去指向存放数据的位置,而p意思是取(p指向的内存位置的数据)&…...
5.33 综合案例2.0 -ESP32拍照上传阿里云OSS
综合案例2.0 - ESP32拍照上传阿里云OSS案例说明连线功能实现1.阿里云平台连接2.OSS对象存储服务3.ESP32-CAM开发环境4.代码ESP32-CAM开发板代码HaaS506开发板代码测试数据转图片方法案例说明 使用ESP32拍照,将照片数据上传阿里云OSS(通过4G网络上传)。 …...
java无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “…...
测试用例设计工作中的应用
1. 等价类划分 常见的软件测试面试题划分等价类: 等价类是指某个输入域的子集合.在该子集合中,各个输入数据对于揭露程序中的错误都是等效的.并合理地假定:测试某等价类的代表值就等于对这一类其它值的测试.因此,可以把全部输入数据合理划分为假设干等价类,在每一个等价类中取一…...
leetcode 困难 —— 数字 1 的个数(简单逻辑题)
(害,做题是真的慢,这面试给我这题我估计就傻了) 题目: 给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。 题解: 首先看看整数范围 0 < n < 10^9 不能遍历࿰…...
关于JSON
<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title></title> </head> <body> <script> /* 1、JSON的英文全称:Java…...
Apifox-接口调用、自动化测试工具
Apifox简介 Apifox 的定位是Postman Swagger Mock JMeter,具有API文档管理、API调试、API Mock、API 自动化测试等功能。可以通过一种工具解决之前使用多种工具的数据同步问题。高效、及时、准确! 安装 Apifox的安装非常方便,直接下载安…...
Vue一个项目兼容每个省份的个性化需求
开发环境及打包指令 后拼上省份区划"serve:henan": "yarn && vue-cli-service serve -o --encryptSM2 --zone41","serve:hunan": "yarn && vue-cli-service serve -o --encryptSM2 --zone43","serve:guizhou&quo…...
npm install报错 npm ERR! 的解决办法
以下是四种常见的npm ERR及解决方式错误一、npm ERR! A complete log of this run can be found in:npm ERR!C:\Users\nanyi\AppData\Roaming\npm-cache_logs\2021-09-17T08_58_23_413Z-debug.l查看错误日志,错误日志就在上面展示的C:\Users…这里如果发现错误日志里…...
echarts修改饼图,环形图的圆环宽度,大小
echarts修改环形图的圆环宽度,大小 环形图圆环的大小需要通过series-pie. radius属性来修改 radius 饼图的半径。 Array.<number|string>:数组的第一项是内半径,第二项是外半径。每一项遵从上述 number string 的描述。 把数组的第…...
小白系列Vite-Vue3-TypeScript:010-封装svg
上一篇我们介绍了ViteVue3TypeScript项目中mockjs的安装和配置i。本篇我们来介绍封装SVG图标组件。svg特征Preloading所有图标都是在项目运行时生成的,只需要操作一次dom即可。高性能内置缓存,仅在文件被修改时才会重新生成。安装插件vite-plugin-svg-ic…...
卷严重、难度高、激励少,如何适应空投市场新变化
自从空投交互从2020年开始之后,不少人都开始加入到空投交互的行列中,一些项目也因为“格局”的因素,在项目正式上线前都会给早期参与者空投代币,以此吸引大家的关注。但是在越来越多的人加入到撸空投行列之中后,现在整…...
基于Java与JSP的文件上传和下载
概念 当用户在前端页面点击文件上传后,用户上传的文件数据提交给服务器端,实现保存。 文件上传步骤 提交方式: 提供form表单,method必须是post。因为post请求无数据限制。 <form method"post"></form>…...
Gromacs中的g_mmpbsa计算带电底物与蛋白的结合能不准确
g_mmpbsa计算带电底物与蛋白的结合能总是不准确 TOC 在做的两个项目中,利用g_mmpbsa计算带电底物与蛋白的结合能结果非常不可靠,底物带两个硫酸根离子,g_mmpbsa在计算带电的底物与酶的结合能时总是不准确,因此后续若底物带电&…...
【mmrotate】旋转目标检测之训练DOTA数据集
every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 mmrotate训练DOTA数据集记录 1. 正文 1.1 数据准备 数据介绍部分,参考DOTA数据介绍,官方提供了裁剪工具development kit。这里…...
图基本概念
图:顶点和边的集合。无向图:每条边都是无方向的有向图:每条边都是有方向的完全图:任意两个点都有一条边相连稀疏图:有很少边或弧的图稠密图:有较多边或弧的图网:边/弧带权的图邻接:有…...
机器学习基础
一、基本概念 1 学习的概念 1975年图灵奖获得者、1978年诺贝尔经济学奖获得者、著名学者赫伯特.西蒙 (Herbert Simon) 曾下过一个定义: 如果一个系统,能够通过执行某个过程,就此改进了它的性能,那么这个过程就是学习.由此可看出,…...
FreeRTOS-Tickless低功耗模式 | FreeRTOS十四
目录 说明: 一、低功耗模式简介 1.1、STM32低功耗模式 二、Tickless模式 2.1、Tickless模式如何功耗 2.2、Tickless模式设计思想 2.3、为了降低功耗,又不影响系统运行,怎么能做到呢? 三、Tickless模式修改配置 3.1、配置…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
