获取泛型,泛型擦除,TypeReference 原理分析

说明

  1. @author blog.jellyfishmix.com / JellyfishMIX - github
  2. LICENSE GPL-2.0

获取泛型,泛型擦除

  1. 下图中示例代码是一个工具类用于生成 csv 文件,需要拿到数据的类型,使用反射感知数据类型的字段,来填充表字段名。
  2. 可以看到泛型 T 没有类似 getClass() 的方法,因为编译后泛型 T 会被擦除,在字节码中不存在 T 这个类型,所以没办法通过 T 来获取某些信息。方法签名中的 java.util.List<T> 编译后会变成 java.util.List
  3. 解决方式是显式传入 Class<?> clazz 来指定数据类型。

image-20240617174325992

Screenshot 2024-06-18 at 15.17.09

泛型嵌套

  1. Class<?> clazz 只能传递一层数据类型,无法解决泛型嵌套时的数据类型传递问题。
  2. 对于泛型嵌套,例如 List<List<Map<String, Person>>>,这样的类型。如果使用 Class<?> clazz 来传递,只能感知到最外层的 List.class,内层泛型还是会出现泛型擦除的情况。
  3. 完整地传递泛型嵌套,还是需要感知到具体的泛型。

TypeReference 原理分析–感知具体泛型

  1. 出现泛型嵌套情况时,获取完整的泛型,也是序列化组件需要面对的问题。解决方法例如 jackson 提供的 TypeReference。

泛型没有完全擦除

  1. javac 编译后没有把所有持有泛型的位置都做擦除。
  2. 编译后的字节码中,子类的类签名显式指定了传递给父类的泛型。

根据子类获取向父类传递的泛型理论基础

作为 TypeReference 的替代品,定义一个 CustomTypeHandler,通过演义来展示 TypeReference 的原理,

public abstract class CustomTypeHandler<T extends Object> {
}

再定义一个 ChildCustomTypeHandler 子类,继承父类时声明泛型。

public class ChildCustomTypeHandler extends CustomTypeHandler<List<List<Map<String, Person>>>> {
    private String tag;
}

编译项目后,使用 jclasslib(一个 IDEA 查看字节码的插件) 查看 ChildCustomTypeHandler.class 字节码,发现 Attributes -> Signature 属性中,记录了类签名,类签名显式指定了传递给父类的泛型。

image-20240617175527362

class 文件结构

jvm 定义了 u1, u2, u4 三种数据结构来表示 1, 2, 4 字节无符号整数。class 文件采用类似 C 语言的结构体来存储数据,如下所示:

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

中文说明:

魔数(Magic Number)
版本号(Minor&Major Version)
常量池(Constant Pool)
类访问标记(Access Flags)
类索引(This Class)
超类索引(Super Class)
接口表索引(Interfaces)
字段表(Fields)
方法表(Methods)
属性表(Attributes)

类的字节码 Attributes -> Signature 属性中,记录了类签名,类签名会显式指定传递给父类的泛型。这是根据子类获取向父类传递的泛型的理论基础,及 TypeReference 的理论基础

根据子类获取向父类传递的泛型 demo

  1. getActualTypeArguments 可能会存在多个泛型,例如 Map<K,V> 所以会返回 Type[] 数组。
  2. 根据 CustomTypeHandler 的约定,只能向 CustomTypeHandler 传递一个最外层 T,因此这里直接通过[0]拿 T。
  3. 这里拿到的 T 是包含泛型嵌套的。例如子类声明 extends CustomTypeHandler<List<List<Map<String, Person>>>>,这里会拿到 List<List<Map<String, Person>>>
  4. 如果想继续拿嵌套的内层泛型,可以继续调用 ParameterizedType#getActualTypeArguments
public abstract class CustomTypeHandler<T extends Object> {
    protected final Type _type;

    /**
     * 此方法实际由子类调用
     */
    protected CustomTypeHandler() {
        Type superClass = getClass().getGenericSuperclass();
        // sanity check, should never happen
        if (superClass instanceof Class<?>) {
            throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
        }
        /*
         * getActualTypeArguments 可能会存在多个泛型,例如 Map<K,V> 所以会返回 Type[] 数组
         * 根据 CustomTypeHandler 的约定,只能向 CustomTypeHandler 传递一个最外层 T,因此这里直接通过[0]拿 T。
         * 这里拿到的 T 是包含泛型嵌套的。例如子类声明 extends CustomTypeHandler<List<List<Map<String, Person>>>>,这里会拿到 List<List<Map<String, Person>>>
         * 如果想继续拿嵌套的内层泛型,可以继续调用 ParameterizedType#getActualTypeArguments
         */
        _type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public Type getType() {
        return this._type;
    }
}

扩展阅读

  1. java Type 接口 https://blog.csdn.net/lvxiangan/article/details/94836504

img

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/721903.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LabVIEW开发为什么沟通需求非常重要

在LabVIEW开发项目中&#xff0c;需求沟通是项目成功的基石。以下是需求沟通的重要性及其原因&#xff1a; 明确项目目标&#xff1a; 定义清晰的目标&#xff1a;通过与用户的沟通&#xff0c;可以明确项目的目标和范围&#xff0c;确保开发团队理解用户的实际需求&#xff0c…

JavaFX DatePicker

JavaFX DatePicker允许从给定日历中选择一天。DatePicker控件包含一个带有日期字段和日期选择器的组合框。JavaFX DatePicker控件使用JDK8日期时间API。 import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.DatePicker; import j…

SpringBoot的事务注解

SpringBoot的事务注解 在Spring Boot应用中&#xff0c;事务管理是一个关键的部分&#xff0c;尤其是当涉及到数据库操作时。Spring Boot提供了强大的事务管理支持&#xff0c;使得开发人员可以通过简单的注解来控制事务的边界和行为。本文将介绍如何在Spring Boot中使用事务注…

Go - 4.数组和切片

目录 一.引言 二.定义 1.基础定义 2.引申理解 三.实战 1.估算切片的长度与容量 2.切片的切片长度与容量 四.拓展 1.估算切片容量的增长 2.切片底层数组的替换 五.总结 一.引言 本文主要讨论 Go 语言的数组 array 类型和切片 slice 类型。主要从二者的使用方法&…

C++ 65 之 模版的局限性

#include <iostream> #include <cstring> using namespace std;class Students05{ public:string m_name;int m_age;Students05(string name, int age){this->m_name name;this->m_name age;} };// 两个值进行对比的函数 template<typename T> bool …

PCIe学习——重点提纲

PCIe学习-重点提纲 基础知识 计算机架构基础总线系统概述PCI vs PCI-X vs PCIe PCIe 概述 PCIe 的发展历史PCIe 与其他总线的对比PCIe 的优势和应用场景 PCIe 体系结构 PCIe 分层模型 物理层&#xff08;Physical Layer&#xff09;数据链路层&#xff08;Data Link Layer&…

在 KubeSphere 上快速安装和使用 KDP 云原生数据平台

作者简介&#xff1a;金津&#xff0c;智领云高级研发经理&#xff0c;华中科技大学计算机系硕士。加入智领云 8 余年&#xff0c;长期从事云原生、容器化编排领域研发工作&#xff0c;主导了智领云自研的 BDOS 应用云平台、云原生大数据平台 KDP 等产品的开发&#xff0c;并在…

[Java基本语法] 常量变量与运算符

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏:&#x1f355; Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;线程与…

解决Java项目运行时错误:“Command line is too long”

在开发Java应用的过程中&#xff0c;你可能偶尔会遇到“Error running ‘Application’: Command line is too long”的问题。这是因为Java虚拟机&#xff08;JVM&#xff09;在启动时&#xff0c;如果传递给它的类路径&#xff08;classpath&#xff09;过长&#xff0c;超过了…

1080 MOOC期终成绩

solution 输出合格的学生信息&#xff0c;其中所谓合格需要同时满足 在线编程成绩>200总评成绩>60 总评计算方法为 当期中成绩>期末成绩时&#xff0c;总评期中成绩0.4期末成绩0.6否则&#xff0c;总评期末成绩 通过map建立学号和学生信息间的关联&#xff0c;否则直…

YOLOv10改进 | 注意力篇 | YOLOv10引入Polarized Self-Attention注意力机制

1. Polarized Self-Attention介绍 1.1 摘要:像素级回归可能是细粒度计算机视觉任务中最常见的问题,例如估计关键点热图和分割掩模。 这些回归问题非常具有挑战性,特别是因为它们需要在低计算开销的情况下对高分辨率输入/输出的长期依赖性进行建模,以估计高度非线性的像素语…

AI引领数字安全新纪元,下一代身份基础设施如何帮助企业破局?

近日&#xff0c;Open AI正式发布面向未来人机交互范式的全新大模型GPT-4o&#xff0c;具有文本、语音、图像三种模态的理解力&#xff0c;无疑代表着人工智能技术的又一重大跃进。 人工智能技术领域不断创新和发展&#xff0c;为各行各业带来巨大的生产变革和经济增长的同时&…

RockChip Android12 Settings二级菜单

一:概述 本文将针对Android12 Settings的二级菜单System进行说明。 二:System 1、Activity packages/apps/Settings/AndroidManifest.xml <activityandroid:name=".Settings$SystemDashboardActivity"android:label="@string/header_category_system&quo…

关于Hutool的模块使用说明方法

Hutool包含多个模块&#xff0c;每个模块针对特定的功能需求提供支持&#xff1a;• hutool-aop&#xff1a;JDK动态代理封装&#xff0c;为非IOC环境提供切面支持。• hutool-bloomFilter&#xff1a;布隆过滤器&#xff0c;提供基于Hash算法的实现。• hutool-cache&#xff…

Day 26:2288. 价格减免

Leetcode 2288. 价格减免 句子 是由若干个单词组成的字符串&#xff0c;单词之间用单个空格分隔&#xff0c;其中每个单词可以包含数字、小写字母、和美元符号 ‘$’ 。如果单词的形式为美元符号后跟着一个非负实数&#xff0c;那么这个单词就表示一个 价格 。 例如 “$100”、…

Corrupt JPEG data: 2 extraneous bytes before marker 0xd9

场景 异常&#xff1a;Corrupt JPEG data: 2 extraneous bytes before marker 0xd9 python语言&#xff0c;CV2读图像数据集&#xff0c;训练目标检测模型。在数据集分批送入模型训练过程中&#xff0c;出现大片图片异常情况。 &#xff08;建议直接去看修复图像方法二&…

做一个30点的四面体结构

这个结构有7层&#xff0c;每层有1&#xff0c;1&#xff0c;3&#xff0c;3&#xff0c;6&#xff0c;6&#xff0c;10个点&#xff0c;共30个点。 30个点的坐标是 {0.0,0.0,6.12372435695795}, {0.0,0.0,0.0}, {-2.8868,-5.0,-2.04124145231931}, {-2.8868,5.0,-2.0412414…

设计神器创作利器矢量图形编辑软件CorelDRAW2024重磅发布啦!

CorelDRAW2024&#xff0c;一款让你的创作无限可能的神器&#xff01;&#x1f3a8;✨ 你是否曾经在寻找一款功能强大、操作简便的设计软件&#xff1f;那么&#xff0c;让我来为你种草一款神奇的产品——CorelDRAW2024&#xff01;&#x1f389;&#x1f929; CorelDRAW全系列…

客户为先,自研为基 | YashanDB产品进化之旅

作为一款全自研的数据库产品&#xff0c;崖山数据库致力于构建自主可控的全栈数据库产品体系&#xff0c;以满足行业用户多元化的需求。 从第一行代码到完整的数据库产品体系&#xff0c;我们为何选择从0到1自研数据库&#xff1f;我们经历了哪些关键的产品节点&#xff1f;每个…

等保2.0对于物联网设备的漏洞管理还有哪些规定?

等保2.0针对物联网设备的漏洞管理&#xff0c;主要规定了以下几个方面&#xff1a; 1. 漏洞发现与识别&#xff1a;要求定期进行漏洞扫描和评估&#xff0c;利用专业的漏洞扫描工具和安全服务&#xff0c;及时发现物联网设备及其软件中的安全漏洞。这包括但不限于操作系统、应…