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、配置…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...
Unity VR/MR开发-VR开发与传统3D开发的差异
视频讲解链接:【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...
React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构
React 实战项目:微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇!在前 29 篇文章中,我们从 React 的基础概念逐步深入到高级技巧,涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...
