Java Type接口出现的原因以及它和泛型的关系

news2024/11/18 17:43:15

Java泛型很多人都用过,但是对于其中的原理可能很多人可能都不太清楚。

首先给出一个结论:Java的泛型是伪泛型,因为JVM在编译以后会将所有泛型参数擦除掉,这个就叫类型擦除。

下面用一段代码来证明,毕竟千言万语BB不如一句代码来的直接:

      List<String> list1 = new ArrayList<>();
      List<Integer> list2 = new ArrayList<>();
      System.out.println(list1.getClass() == list2.getClass());

结果出人意料:竟然是True。

经过JVM编译以后,两个List的class都返回了原始类型List,并没有带上各自的泛型参数。

为什么会这样?!

这是因为泛型是Java 1.5以后才出现的,所以以前的java代码根本就没有尖括号泛型这玩意,

为了保持兼容性,Java在运行时简单粗暴的把泛型信息给擦除掉了!

不得不说,Java的这种做法相当差劲,简直就是垃圾设计!

但是以上代码的泛型信息真的全部都消失了吗。

让我们反编译字节码文件看一下:

 javap -c -v TestTypeErace.class  // java反汇编命令

输出了一堆东西,但是重点看一下 如下信息:

 看见没有:虽然JVM擦除了对象的泛型参数,但是在编译阶段:泛型信息仍然保存到了java字节码文件中的LocalVariableTypeTable这张表里面。

所以Type接口这玩意儿就来了:Type接口可以通过反射获取到泛型类的泛型信息!

这就是Type接口出现的真正原因!

 以下是Type接口的层级结构图

不得不说Java这门破语言的概念是相当多,然后起的名字又是狗屁不通,不好理解!

简单说一下:Type接口作为所有类型的总接口,包括了原始类型和泛型,反正就是生搬硬套的搞出了这么一个东西,为了保证兼容性,向低版本兼容。

下面简单解释一下这几个子接口的作用:

Class: 原生类型的Class对象,也就是我们日常所有的类/接口,包括枚举,数组,注解等,但不包括基本类型:如int, float等。

Class类的对象包含了某个被加载类的结构。一个被加载的类对应一个Class对象

当一个类/接口被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象==》 这里实际涉及到classLoader的累加器机制和它的源码部分。

备注:Java所有的类和接口被JVM装载以后,都会生成一个跟类相关的java.lang.Class对象(字节码文件),每个类型都会有一个Class对象,Class类的对象只能由JVM创建,无法通过new来创建,当类/接口被加载时,就会被JVM自动生成Class对象,这也是反射的基础。

剩下的几种全部针对泛型:

这里就不用英文释义了,因为命名实在太差劲了,什么叫参数化类型,完全扯淡!垃圾命名!

TypeVariable: 简单说就是泛型尖括号里面的类型比如Map<T,V>, TypeVariable指的就是T和V

ParameterizedType: 声明带有泛型的类型: 也就是带有尖括号的类型,比如List<T>, Map<T,V>,

只要带尖括号就是ParameterizedType,也就是泛型类型

GenericArrayType: 简单说就是泛型数组: 首先他是一个数组,然后数组的变量是泛型

就这么简单,举几个例子:

List<String> ,  T[],  Class<T>[] 这几个都是GenericArrayType

WildcardType: 通配符类型,这个不多说了,跟Class<?>差不多,实际用的很很少

这里重点讲讲ParameterizedType的两个重要Api:

getActualTypeArguments: 获取泛型参数列表:因为可能存在多个泛型,比如 SuperClass<T, V>,所以会返回 Type[] 数组

getRawType: 获取泛型尖括号前面的类型

总的来说Type的出现主要是为了配合泛型的反射操作,所以下面用一段代码简单证明一下:

public class BaseObj<T,U> {
    private List<T> items;
    private List<String> names;
    private BaseObj baseObj1;
    private BaseObj<T,U> baseObj2;
    private List list;
    private Map<T, U> map = new HashMap<>();
    private T t;


    private <E> T getItem(T t, E e) {
        return t;
    }

    public BaseObj<T,U> test(List<T> items, BaseObj<Integer,Integer> nums, T t) {
        return null;
    }

    public static void main(String[] args) {

        System.out.println("---------------输出泛型类尖括号里的类型----------------------------------------");
        // 用当前类名获取class对象无需getClass方法,只要类的实例才需要getClass方法
        Class<BaseObj> baseObjClass = BaseObj.class;
        TypeVariable<Class<BaseObj>>[] typeParameters = baseObjClass.getTypeParameters();
         for (TypeVariable<Class<BaseObj>> typeParameter : typeParameters) {
            System.out.println(typeParameter);
        }
        System.out.println("----------------输出泛型类属性中的泛型类型------------------------------------------------------");

        Field[] declaredFields = BaseObj.class.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println(field.getType() + "=" + field.getName());
            Type genericType = field.getGenericType(); // 获取完整泛型:类名<泛型>

            if (genericType instanceof ParameterizedType) {
                ParameterizedType pType = (ParameterizedType) genericType;
                System.out.println("Typename :" + pType.getTypeName());  // 获取完整泛型:全路径类名<泛型>
                System.out.println("rawType :" + pType.getRawType()); // 获取泛型尖括号前面的类型
                System.out.println("ownerType :" + pType.getOwnerType()); // 获取父类类型

                // 获取泛型尖括号<>里面的实际数据类型,因为可能存在多个泛型,比如 SuperClass<T, V>,所以会返回 Type[] 数组
                Type[] types = pType.getActualTypeArguments();
                System.out.println("泛型参数列表: " + Arrays.asList(types));

                System.out.println("------------------------------------------");
                continue;
            }
            System.out.println("成员变量:"+field.getName() + "不是泛型变量!");
            System.out.println("genericType =>" + genericType);
            System.out.println("------------------------------------------");
        }

    }

可能你看的会有点懵逼,不要紧,你结合之前的文字说明,再结合代码注释就能慢慢看懂,因为我也是这么理解来的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【软考数据库】第三章 数据结构与算法

目录 3.1 数据结构 3.1.1 线性结构 3.1.2 数组 3.1.3 矩阵 3.1.4 树与二叉树 3.1.5 图 3.2 查找 3.2.1 顺序查找 3.2.2 折半查找 3.2.3 哈希表 3.3 排序 3.3.1 直接插入排序 3.3.2 希尔排序 …

Win10任务栏卡死怎么办?这3个方法快收藏!

案例&#xff1a;win10任务栏卡死 【姐妹们&#xff0c;我的win10任务栏一直卡着&#xff0c;我完全没法使用计算机了&#xff0c;遇到这种情况&#xff0c;我应该怎么做呢&#xff1f;求大家给我支支招&#xff01;感谢感谢&#xff01;】 我们使用电脑的过程中&#xff0c;…

MyBatis的添加和简单使用

什么是MyBatis mybatis是一个方便我们更简单的操作数据库的框架&#xff0c;让我们不用再使用JDBC操作数据库。 MyBatis的创建 老项目添加mybatis&#xff0c;首先要安装好editstarters插件&#xff0c;然后在pom.xml中右键generate选择edit插件&#xff0c;注意不仅要添加m…

多维时序 | MATLAB实现BO-CNN-BiLSTM贝叶斯优化卷积双向长短期记忆网络数据多变量时间序列预测

多维时序 | MATLAB实现BO-CNN-BiLSTM贝叶斯优化卷积双向长短期记忆网络数据多变量时间序列预测 目录 多维时序 | MATLAB实现BO-CNN-BiLSTM贝叶斯优化卷积双向长短期记忆网络数据多变量时间序列预测效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 基于贝叶斯优化卷…

C/C++|物联网开发入门+项目实战|宏定义|数据声明|位操作|类型修饰符|访问固定内存位置|嵌入式C语言高级|常见面试题目讲解-学习笔记(13)

文章目录 常见面试题目讲解宏定义数据声明类型修饰符的使用总结位操作访问固定内存位置 参考&#xff1a; 麦子学院-嵌入式C语言高级-C语言函数的使用-常见面试题目讲解 参考&#xff1a; 嵌入式程序员应该知道的0x10个基本问题 常见面试题目讲解 宏定义 1 .用预处理指令#d…

ERD Online 4.1.0对接ChatGPT,实现AI建模、SQL自由

ERD Online 是全球第一个开源、免费在线数据建模、元数据管理平台。提供简单易用的元数据设计、关系图设计、SQL查询等功能&#xff0c;辅以版本、导入、导出、数据源、SQL解析、审计、团队协作等功能、方便我们快速、安全的管理数据库中的元数据。 4.1.0 ❝ :memo: fix(erd): …

CARIS11.3使用一段时间后的经验和总结

虽然CARIS11.4存在一些小bug&#xff0c;但CARIS11.3使用没有什么问题&#xff0c;相对于CARIS9而言&#xff0c;在导入数据和程序界面有些改进。用过CARIS9的同学都知道其建立项目和导入数据的步骤比较繁琐。而CARIS11.3导入数据的过程比较简洁&#xff0c;基本步骤如下&#…

把阿里大鸟花3个月时间整理的软件测试面经偷偷给室友,差点被他开除了···

写在前面 “这份软件测试面经看起来不错&#xff0c;等会一起发给他吧”&#xff0c;我看着面前的面试笔记自言自语道。 就在这时&#xff0c;背后传来了leder“阴森森”的声音&#xff1a;“不错吧&#xff0c;我可是足足花了三个月整理的” 始末 刚入职阿里的我收到了大学…

牛客网Verilog刷题——VL2

牛客网Verilog刷题——VL2 题目答案 题目 要求用verilog实现两个串联的异步复位的T触发器的逻辑&#xff0c;如下图所示。   模块的输入输出信号如下表&#xff0c;需要注意的是&#xff1a;这里rst是低电平复位&#xff0c;且采用异步复位的方式复位。 信号类型输入/输出c…

2023年淮阴工学院五年一贯制专转本大学语文考试大纲

2023年淮阴工学院五年一贯制专转本大学语文考试大纲 一、考试目标 淮阴工学院五年一贯制高职专转本入学考试秘书学专业《大学语文》考试是我校为招收五年一贯制高职专转本学生设置的具有选拔性质的考试科目。其目的是科学、公平、有效地测试考生是否具备攻读秘书学本科学位所…

( “树” 之 BST) 530. 二叉搜索树的最小绝对差 ——【Leetcode每日一题】

二叉查找树&#xff08;BST&#xff09;&#xff1a;根节点大于等于左子树所有节点&#xff0c;小于等于右子树所有节点。 二叉查找树中序遍历有序。 ❓ 530. 二叉搜索树的最小绝对差 难度&#xff1a;简单 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同…

特征选择算法 | Matlab 基于无限潜在特征选择算法(ILFS)的分类数据特征选择

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 特征选择算法 | Matlab 基于无限潜在特征选择算法(ILFS)的分类数据特征选择 部分源码 %

Vite 4.3 is out!

原文地址 本次迭代中&#xff0c;我们专注于改善开发服务器的性能。我们优化了解析逻辑&#xff0c;改进了热路径&#xff0c;并实现了更智能的缓存&#xff0c;用于查找 package.json、TS 配置文件和解析的 URL 等。 你可以在 Vite 的贡献者之一的博客文章中详细了解本次性能…

数据结构之二分搜索树

树在我们底层结构中是被广泛运用的,但是为什么会选择它却是我们需要了解的东西,接下来 让我们一起走进树的世界 请看下图&#xff1a; 在我们生活中&#xff0c;有很多关于树的存在&#xff0c;比如电脑中的磁盘&#xff08;C D盘&#xff09;&#xff0c;在文章中写的目录都是…

LangChain与大型语言模型(LLMs)应用基础教程:记忆力组件

如果您还没有看过我之前写的两篇博客&#xff0c;请先看一下&#xff0c;这样有助于对本文的理解&#xff1a; LangChain与大型语言模型(LLMs)应用基础教程:Prompt模板 LangChain与大型语言模型(LLMs)应用基础教程:信息抽取 LangChain与大型语言模型(LLMs)应用基础教程:角色…

在线甘特图制作教程

在线甘特图制作教程 很多的甘特图工具都是需要下载到本地&#xff0c;并且做好了之后也不方便分享给别人。给大家分享一个在线的甘特图制作工具 不需要登录注册 知竹甘特图 https://www.yxsss.com/ 打开知竹甘特图 https://www.yxsss.com/gatt/3b7d1ecb7211b9473e7d1ecb72 …

015:Mapbox GL绘制修改多边形,实时更新面积

第015个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中添加draw组件,绘制多边形,编辑多边形,实时显示面积值。这里使用turf来计算面积值。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共92行)安装…

ASP.NET Core MVC 从入门到精通之wwwroot和客户端库

随着技术的发展&#xff0c;ASP.NET Core MVC也推出了好长时间&#xff0c;经过不断的版本更新迭代&#xff0c;已经越来越完善&#xff0c;本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容&#xff0c;适用于初学者&#xff0c;在校毕业生&#xff0c…

remvw布局

文章目录 rem&vw布局rem布局方式原理使用第三框架 vw布局方式原理使用 rem&vw混合布局方式vw方案案例 rem&vw布局 rem布局方式 原理 rem是相对于根元素&#xff08;html元素&#xff09;的字体大小来计算的&#xff0c;因此可以根据不同的屏幕尺寸和设备类型自动…

EF Core入门

文章目录 前言一、EF Core环境搭建二、基本的增删改查1.增加数据2.查询数据3.修改数据&#xff0c;删除数据 前言 EF Core是微软官方提供的ORM框架。EF Core不仅可以操作Microsoft SQL Server、MySQL、Oracle、PostgreSQL等数据库&#xff0c;而且可以操作Azure Cosmos DB等No…