当前位置: 首页 > ds >正文

深入学习 Java 泛型实现方式:擦除法!

全文目录:

    • 开篇语
    • 前言
    • 1. 泛型简介
      • 泛型的基本语法
    • 2. 泛型的实现方式:擦除法
      • 擦除法的工作原理
      • 擦除的例子
      • 泛型的类型擦除例子
      • 擦除与泛型边界
    • 3. 擦除法的影响
      • 3.1 反射中的限制
      • 3.2 类型转换和类型安全
      • 3.3 泛型的变通方法
    • 4. 总结
    • 文末

开篇语

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

  在 Java 中,泛型是一个非常重要的概念,它使得代码能够更加灵活和类型安全。你可能已经在开发中使用过泛型,它允许你在编译时指定类型,而在运行时保证类型安全。但你是否了解过 Java 泛型的实现方式?很多人可能并不知道,Java 泛型的实现是通过一种叫做“擦除法”(Type Erasure)的方法实现的。

  今天,我们将深入探索 Java 泛型是如何实现的,特别是“擦除法”背后的原理和实际应用。通过这篇文章,你将更加清楚地理解泛型在 Java 中的工作原理,以及为什么 Java 需要使用擦除法来实现泛型。

1. 泛型简介

  泛型允许你在类、接口和方法中使用类型参数。它使得代码能够在编译时就确定类型,从而避免了运行时的类型转换错误。泛型的核心思想就是类型参数化,也就是说你可以在不具体指定类型的情况下,定义类、接口和方法。

泛型的基本语法

class Box<T> {private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}
}

在上面的例子中,Box 类定义了一个泛型类型 T,它可以接受任何类型的值。当你使用这个类时,你可以指定具体的类型:

Box<Integer> intBox = new Box<>();
intBox.setValue(100);Box<String> strBox = new Box<>();
strBox.setValue("Hello, World!");

通过泛型,Box 类可以处理不同类型的数据,而不需要写多个不同类型的类。

2. 泛型的实现方式:擦除法

  在 Java 中,泛型是通过 擦除法 来实现的。擦除法的核心思想是,在编译时,Java 编译器会移除所有泛型类型的具体信息,并将泛型类型替换为原始类型。也就是说,泛型在编译后的字节码中并不存在。泛型类型的“擦除”发生在编译时,而在运行时,JVM 并不知道泛型的类型参数是什么。

擦除法的工作原理

  1. 类型擦除:Java 编译器会将所有的泛型类型替换为它们的原始类型。例如,Box<Integer>Box<String> 会被擦除为 Box,这意味着在编译后的字节码中,Box<Integer>Box<String> 没有任何区别。
  2. 类型参数替换:对于泛型类型的实例化,编译器会将泛型类型的类型参数替换为原始类型或上界类型。例如,Box<T> 会被转换为 Box<Object>,如果指定了类型上限,泛型类型会被替换为该上限类型。
  3. 类型安全保证:擦除法保证了 Java 中的类型安全。在运行时,类型信息已经丢失,但 Java 会通过类型检查和转换来确保类型的安全性。

擦除的例子

假设我们有一个简单的泛型类:

class Box<T> {private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}
}

在编译后,Box<Integer>Box<String> 的字节码将会变成类似以下形式:

class Box {private Object value;public Object getValue() {return value;}public void setValue(Object value) {this.value = value;}
}

可以看到,所有的泛型信息(例如 <T>)已经被移除了,T 被替换为 Object。这就是 Java 中泛型的“擦除”过程。

泛型的类型擦除例子

为了更好地理解擦除法,来看一个具体的例子:

class Pair<T, U> {private T first;private U second;public Pair(T first, U second) {this.first = first;this.second = second;}public T getFirst() {return first;}public U getSecond() {return second;}
}public class Test {public static void main(String[] args) {Pair<String, Integer> pair = new Pair<>("Hello", 10);// 输出:Hello, 10System.out.println(pair.getFirst() + ", " + pair.getSecond());}
}

编译后,Pair<String, Integer>Pair<Double, String> 的字节码会被擦除为如下形式:

class Pair {private Object first;private Object second;public Pair(Object first, Object second) {this.first = first;this.second = second;}public Object getFirst() {return first;}public Object getSecond() {return second;}
}

擦除与泛型边界

在泛型中,我们通常可以设置类型的边界,例如:

class Box<T extends Number> {private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}
}

在上述代码中,T 的类型被限定为 Number 的子类,编译后,T 会被擦除为 Number 类型:

class Box {private Number value;public Number getValue() {return value;}public void setValue(Number value) {this.value = value;}
}

这说明泛型类型 T 被擦除为 Number,即使我们传入的是 IntegerDouble 等具体类型,最终 Box 仍然能够接受 Number 类型的值。

3. 擦除法的影响

3.1 反射中的限制

由于泛型类型在运行时被擦除,使用反射时无法获取泛型的实际类型。例如,下面的代码会失败,因为 List<String>List<Integer> 在运行时都被擦除为 List

import java.lang.reflect.*;public class Test {public static void main(String[] args) throws NoSuchFieldException {Field field = Pair.class.getDeclaredField("first");System.out.println(field.getGenericType());  // 输出:T}
}

上述代码中的 Pair<String, Integer>Pair<Double, String> 在运行时无法知道类型参数 TU 的实际类型。

3.2 类型转换和类型安全

由于擦除法的存在,在使用泛型时,编译器会进行类型检查,确保类型转换的安全性。例如:

List<String> list = new ArrayList<>();
list.add("Hello");
String value = list.get(0);  // 类型安全,编译时就检查通过

虽然在编译时可以保证类型的安全性,但在运行时,由于泛型信息已被擦除,JVM 只会看到原始类型的 List

3.3 泛型的变通方法

为了弥补泛型擦除带来的限制,Java 引入了通配符和类型参数的边界约束。例如,使用 ? extends T? super T 来表示可以接收 T 的子类型或父类型,从而增强泛型的灵活性。

public static void printList(List<? extends Number> list) {for (Number number : list) {System.out.println(number);}
}

这种方式允许我们在运行时处理不同的类型,而无需依赖具体的类型参数。

4. 总结

  Java 泛型的实现方式主要依赖于 擦除法,即在编译时移除泛型类型的具体信息,在运行时只保留原始类型或上界类型。擦除法能够有效地保持 Java 的兼容性,并且提供类型安全,但它也带来了反射时无法获取泛型类型、类型转换不方便等问题。

通过理解擦除法的工作原理,我们可以更好地利用 Java 泛型提供的类型安全性,并在开发过程中根据实际需求选择合适的泛型实现方式。希望本文能够帮助你深入理解 Java 泛型的实现机制,为你在开发中解决实际问题提供指导。

… …

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

… …

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。


版权声明:本文由作者原创,转载请注明出处,谢谢支持!

http://www.xdnf.cn/news/5973.html

相关文章:

  • 43、Server.UrlEncode、HttpUtility.UrlDecode的区别?
  • 物理:篮球为什么能被拍起来?
  • .Net HttpClient 使用Json数据
  • Centos7安装部署wordpress个人博客保姆级教程
  • iVX 研发基座:大型系统开发的协作与安全架构实践
  • 基于MATLAB的生物量数据拟合模型研究
  • 云蝠智能大模型呼叫优势:技术驱动全链路升级,重塑智能交互服务新体验
  • 前端性能优化3:深入分析 Web Worker 和 Service Worker
  • 【源码+文档+调试讲解】驾校报名小程序2
  • python打卡day24
  • ppy/osu构建
  • window 显示驱动开发-创建分配时指定段
  • 块设备代码分析
  • 测试集群的功能-执行wordcount程序
  • uniapp|实现获取手机摄像头权限,调用相机拍照实现人脸识别相似度对比,拍照保存至相册,多端兼容(APP/微信小程序)
  • 什么情况会导致JVM退出?
  • 【机器学习赋能的智能光子学器件系统研究与应用】
  • 界面组件DevExpress WPF中文教程:Grid - 如何自定义Band Header外观?
  • 《内网渗透测试:绕过最新防火墙策略》
  • ZYNQ实战:可编程差分晶振Si570的配置与应用指南
  • 人工智能基础知识笔记九:模型评估的指标
  • OpenAI官方指南,详细解释了何时使用哪种AI模型
  • amd架构主机构建arm架构kkfileview
  • vue3学习——侦听器
  • 从零开始掌握FreeRTOS——目录
  • Java后端快速生成验证码
  • Python查询ES错误ApiError(406, ‘Content-Type ...is not supported
  • vr视频制作攻略(VR视频制作基础知识)
  • 漏桶算法的实际应用案例:数据库批量写入流量控制
  • 基于智能家居项目 解析DHT11温湿度传感器