关于泛型的类型擦除

2023-05-16

关于泛型的类型擦除

什么是泛型?

在探讨类型擦除之前,我们还是先了解一下,泛型的概念。

泛型是为了参数化类型,定义方法时传入形参,而调用方法时使用形参,参数化类型就是由确定的类型参数化,改为不确定的类型,而在调用时使用具体类型的参数,从而实现解耦。这种参数类型可以用于类、接口、方法中,分别被称为泛型类、泛型接口和泛型方法。

为什么使用泛型

参考文章

这里的栗子是别人写的,个人觉得挺形象的,粘贴一下

试想你需要一个简单的容器类,或者说句柄类,比如要存放一个苹果的篮子,那你可以这样简单的实现:

class Fruit{}
class Apple extends Fruit{}

class Bucket{
    private Apple apple;
   
     public void set(Apple apple){
        this.apple = apple;
    }
   
    public Apple get(){
        return this.apple;
    }
}

这样一个简单的篮子就实现了,但问题是它只能存放苹果,之后又出现了另外的一大堆水果类,那你就不得不为这些水果类分别实现容器:

class Fruit{}
class Apple extends Fruit{}
class Banana extends Fruit{}
class Orange extends Fruit{}

class BucketApple{
    private Apple apple;

    public void set(Apple apple){
        this.apple = apple;
    }

    public Apple get(){
        return this.apple;
    }
}

class BucketBanana{
    private Banana banana;

    public void set(Banana banana){
        this.banana = banana;
    }

    public Banana get(){
        return this.banana;
    }
}
class BucketOrange{
    private Orange orange;

    public void set(Orange orange){
        this.orange = orange;
    }

    public Orange get(){
        return this.orange;
    }
}

然后你发现你其实在做大量的重复劳动。所以你幻想你的语言编译器要是支持某一种功能,能够帮你自动生成这些代码就好了。

不过在祈求让编译器帮你生成这些代码之前,你突然想到了Object能够引用任何类型的对象,所以你只要写一个Object类型的Bucket就可以存放任何类型了。

class Bucket{
    private Object object;

    public void set(Object object){
        this.object = object;
    }

    public Object get(){
        return this.object;
    }
}

但是问题是这种容器的类型丢失了,你不得不在输出的地方加入类型转换:

Bucket appleBucket = new Bucket();
bucket.set(new Apple());
Apple apple = (Apple)bucket.get();

而且你无法保证被放入容器的就是Apple,因为Object可以指向任何引用类型。

这个时候你可能又要祈求编译器来帮你完成这些类型检查了。

说到这里,你应该明白了泛型要保证两件事,

第一:我只需要定义一次类,就可以被“任何”类型使用,而不是对每一种类型定义一个类。

第二:保存对象类型,而不用每次都强转

泛型的实现原理

通过以上的栗子,我们对泛型有一个大致的了解,但是泛型是怎么来实现以上特性的呢?这里就引入这篇文章的标题的内容,‘类型擦除

我这里自己写了一个泛型java类,很简单

public class TestGeneric<T> {
    public T instance;
    public TestGeneric(T hello) {
        instance=hello;
        System.out.println(hello.getClass().getCanonicalName());
    }
}

编译以后生成的class文件

public class TestGeneric<T> {
    public T instance;

    public TestGeneric(T hello) {
        this.instance = hello;
        System.out.println(hello.getClass().getCanonicalName());
    }
}

对,你没看错就是一样的。但是我们把鼠标放到T上,会看到T扩展自Object,所以泛型根本上还是通过Object来实现的类型扩展。

image-20211104134152110

我们接着看以下泛型在调用时的情况

public class TestGenericCore {
    public void show(){
        Apple apple=new Apple();
        Banana banana=new Banana();
        TestGeneric<Apple> appleTestGeneric=new TestGeneric<>(apple);
      //不用强转就可以直接调用指定类的成员方法,这就是泛型的好处
        appleTestGeneric.instance.showApple();
        TestGeneric<Banana> bananaTestGeneric=new TestGeneric<>(banana);
        bananaTestGeneric.instance.showBanana();
    }
}
public class Apple {
    public void showApple(){}
}
public class Banana {
    public void showBanana(){}
}

这里我们分别传入两个类,并且为了区分,成员方法分别是showApple和showBanana。

我们接着看一下以上TestGenericCore的class文件是怎么样的。

public class TestGenericCore {
    public TestGenericCore() {
    }

    public void show() {
        Apple apple = new Apple();
        Banana banana = new Banana();
        TestGeneric<Apple> appleTestGeneric = new TestGeneric(apple);
        ((Apple)appleTestGeneric.instance).showApple();
        TestGeneric<Banana> bananaTestGeneric = new TestGeneric(banana);
        ((Banana)bananaTestGeneric.instance).showBanana();
    }
}

强转出现……所以泛型最后在编译时也会替我们把隐去的强转加上,可以说是非常贴心了。

至于类型擦除,就是在编译期间,根据我们声明的泛型类型进行静态类型检查,然后再将类型擦除,改为Object。所谓的类型检查就是在对象出现的地方,例如(赋值语句,函数调用的实参和形参检查、return返回的类型必须兼容)判断类型是否符合约束。

因为类型会被擦除,所以以下方法会报错,因为在编译看来,形参是一样的。

image-20211104141122624

总结

好了,本篇只是用来记录一下自己搞懂类型擦除的过程,希望对大家有所帮助。

引用

java为什么要用类型擦除实现泛型

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

关于泛型的类型擦除 的相关文章

随机推荐

  • 阿里巴巴2022届应届生招聘 阿里云存储基础技术 研发&测试&算法

    学弟学妹们好 xff0c 我们是阿里云存储基础技术团队 xff0c 正在进行2022届校招 团队业务核心 xff0c 团队氛围nice xff0c 对于每位实习同学都会配师兄专职辅导 本次招聘为部门直招 xff0c 简历直接推给Leader
  • Android源码下载与编译之emulator与adb

    最近碰到一个问题 xff0c 就是Android的emulator只能打开16个 xff0c 在网上找到文章有说adb连接数量是16个的 xff0c 但是没说这个avd也只能16个 xff08 当然是在一台电脑上 xff09 于是 xff0
  • 自动化刷EOS资金盘(类Fomo3D)游戏

    7月底在以太坊的Fomo3D游戏异常火爆 xff0c 两三天后 xff0c 在EOS主网上 xff0c 国内各种仿品相继上线 xff0c 留心观察 xff0c 这些游戏里都有着自动化交易的身影 本文为你分析如何实现一个简单的EOS自动化交易
  • 毕业后就是程序员——我的阿里、金山、中华、腾讯、360、网易面试总结(三)

    接着上篇 xff0c 这篇侧重于具体的笔试 面试的问题 xff0c 至于是哪个公司的就不去追究了 xff0c 但一定是经常问到的 xff0c 而且我嵌入式系统工程师和移动开发工程师都参与了 xff0c 问题要区别看待 xff0c 那么自然就
  • 你投资的那些EOS“侧链”都还好么?

    EOS侧链BOS的启动声势浩大 xff0c 引得一众EOS超级节点与公司参与支持 xff0c 也招来币圈大佬老猫的质疑 xff0c 直言 熊市 xff0c 我劝你善良 xff1b 近几日 xff0c 曾被称作是EOS上首条侧链的FIBOS开
  • 销毁3417万枚EOS与被刺激的EOS价格——理解增发与销毁机制

    eosio saving帐号销毁的3417万枚EOS与REX锁住的6000多万枚EOS xff0c 都刺激不了EOS价格 xff0c 甚至仍有下跌的趋势 xff0c 而几天后一觉醒来 xff0c 不管BTC还是EOS都在疯涨 xff0c 过
  • Error executing aapt: Return code -1073741819

    总会有让人喷血的事情勾起写博客的欲望 xff0c 希望能坚持 折腾了两天的是个小问题 xff0c 就是标题上的Error executing aapt Return code 1073741819 解决的方法也很简单参考1中所述 xff0c
  • EOS的危险信号——记主网上线一周年后

    市值已经跌落到第八 xff0c 除了期待即将推出的社交应用 Voice 能给 EOS 注入新的活力外 xff0c 我已经很难找到 EOS 上的新热点 xff0c 而一些危险的信号 xff0c 可能对 EOS 的发展带来负面的影响 xff0c
  • oracle官网下载JDK速度很慢的解决方案

    华为有个镜像可以下载 xff1a https repo huaweicloud com java jdk
  • 被远程连接的windows一定要设置账号的密码吗

    背景 我们有个windows系统 xff08 方便表述叫P xff0c 比如win10的 xff0c 平时使用的用户名是a xff0c 没有登录密码 xff0c 平时使用P的过程是允许没有密码登录操作系统的 但是 xff0c 如果使用另外一
  • 详解Java线程池参数

    详解Java线程池参数 目前线程池的类一般使用 spring的 xff1a org springframework scheduling concurrent ThreadPoolTaskExecutorJDK的 xff1a java ut
  • python watchdog:监控文件系统事件的Python库

    python watchdog xff1a 监控文件系统事件的Python库和shell工具 watchdog用来监控指定目录 文件的变化 xff0c 如添加删除文件或目录 修改文件内容 重命名文件或目录等 xff0c 每种变化都会产生一个
  • IDEA 使用本地maven仓库 从下载到配置

    记录一下使用过程 环境 xff1a win10 maven3 6 3 idea2019 三个分类 xff0c 可以熟悉的可以跳着看 躺着看都行 一 下载maven 1 下载maven的压缩包 我这里使用的是apache maven 3 6
  • 环形缓冲区(ring buffer),环形队列(ring queue) 原理

    环形缓冲区 ring buffer xff0c 环形队列 ring queue 多用于2个线程之间传递数据 xff0c 是标准的先入先出 FIFO 模型 一般来说 xff0c 对于多线程共享数据 xff0c 需要使用mutex来同步 xff
  • jupyter notebook找不到tensorflow,已解决

    问题描述 xff1a 在debain里安装了jupyter xff0c 安装方法参考https blog csdn net wacebb article details 117407106 spm 61 1001 2014 3001 550
  • Ubuntu删除root密码

    想让root恢复成初始时候一样 xff1a 没有密码 xff1b 切换root账号输入密码错误 xff1b 使用下面的命令 xff1a 删除root账号密码 sudo passwd d root 锁定root账号 sudo passwd l
  • 指针+1的问题

    指针 43 1 并不是指针代表的地址值 43 1 指针变量加1 xff0c 即向后移动1 个位置表示指针变量指向下一个数据元素的首地址 而不是在原地址基础上加1 至于真实的地址加了多少 xff0c 要看原来指针指向的数据类型是什么 char
  • 手把手带你学习Spring框架

    一 介绍Spring框架 1 1学习Spring框架的原因 企业级应用是指那些为商业组织 xff0c 大型企业而创建并部署的解决方案 这些大型企业级应用的结构复杂 xff0c 涉及的外部资源众多 xff0c 事务密集 xff0c 数据规模大
  • Typora+Gitee+PicGo实现markdown图片自动插入

    Typora 43 Gitee 43 PicGo实现markdown图片自动插入 文章目录 Typora 43 Gitee 43 PicGo实现markdown图片自动插入写到前面效果展示搭建步骤下载并安装Picgo安装node js创建g
  • 关于泛型的类型擦除

    关于泛型的类型擦除 什么是泛型 xff1f 在探讨类型擦除之前 xff0c 我们还是先了解一下 xff0c 泛型的概念 泛型是为了参数化类型 xff0c 定义方法时传入形参 xff0c 而调用方法时使用形参 xff0c 参数化类型就是由确定