深入理解Jdk5引入的Java泛型:类型安全与灵活性并存

深入理解Jdk5引入的Java泛型:类型安全与灵活性并存

在这里插入图片描述

​ 在Java的中,有一个强大的工具,它可以让你在编写代码时既保持类型安全,又享受灵活性。**这个工具就是——泛型(Generics)。**本文将引导你深入了解Java泛型的奥秘,让你从此编写更强大、更安全的代码。

基本概念:

泛型是什么?

​ 泛型是Java中的一项强大功能,它允许你编写能够处理多种数据类型的类、接口和方法。通过使用泛型,你可以将数据类型作为参数传递给类或方法,从而实现更高层次的代码重用和类型安全。

Java 泛型(generics)本质是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?

答案是可以使用 Java 泛型

使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。


泛型类和泛型方法

泛型在Java中有两种主要形式:泛型类和泛型方法。泛型类是具有一个或多个类型参数的类,而泛型方法是在调用时可以接受不同类型参数的方法。


泛型类的基本概念

泛型类是一种允许在创建类的实例时指定数据类型的类。通过使用泛型,你可以将类的数据类型参数化,从而使得类可以适用于多种数据类型。让我们来看一个简单的例子:一个通用的容器类Box

public class Box<T> {private T content;public Box(T content) {this.content = content;}public T getContent() {return content;}public void setContent(T content) {this.content = content;}
}

在上面的示例中,我们定义了一个泛型类Box,它可以存储任意类型的数据。T是一个类型参数,它在实例化Box对象时确定。

使用泛型类

现在,让我们看看如何使用泛型类Box来存储不同类型的数据。

public class GenericClassExample {public static void main(String[] args) {Box<Integer> intBox = new Box<>(42);//存储整数int intValue = intBox.getContent();System.out.println("Integer value: " + intValue);Box<String> stringBox = new Box<>("Hello, Stevedash!");//存储字符串String stringValue = stringBox.getContent();System.out.println("String value: " + stringValue);}
}

在上述示例中,我们创建了两个Box对象,一个存储整数,另一个存储字符串。通过使用泛型类,我们可以在编译时就确保数据类型的正确性,避免了在运行时出现类型错误。


类型安全与重用性

泛型类不仅提供了类型安全性,还增加了代码的重用性。你可以轻松地创建适用于不同类型的通用类,减少了代码的冗余。这使得你的程序更加健壮、可维护。


泛型类的限制

虽然泛型类非常强大,但也有一些限制。例如,无法创建泛型数组,也不能使用基本数据类型作为泛型参数(需要使用对应的包装类)。

自定义泛型类

除了使用Java提供的泛型类,你还可以自定义泛型类来满足特定需求。通过实现自己的泛型类,你可以更好地理解泛型的原理,以及如何灵活地使用它。


泛型方法

你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。

下面是定义泛型方法的规则:

  • 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的 )。

  • 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。

  • 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。

  • 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像 int、double、char 等)。


    java 中泛型标记符:

  • E - Element (在集合中使用,因为集合中存放的是元素)

  • T - Type(Java 类)

  • K - Key(键)

  • V - Value(值)

  • N - Number(数值类型)

  • ? - 表示不确定的 java 类型


下面的例子演示了如何使用泛型方法打印不同类型的数组元素:

实例

public class GenericMethodTest
{// 泛型方法 printArray                         public static < E > void printArray( E[] inputArray ){// 输出数组元素            for ( E element : inputArray ){        System.out.printf( "%s ", element );}System.out.println();}public static void main( String args[] ){// 创建不同类型数组: Integer, Double 和 CharacterInteger[] intArray = { 1, 2, 3, 4, 5 };Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };System.out.println( "整型数组元素为:" );printArray( intArray  ); // 传递一个整型数组System.out.println( "\n双精度型数组元素为:" );printArray( doubleArray ); // 传递一个双精度型数组System.out.println( "\n字符型数组元素为:" );printArray( charArray ); // 传递一个字符型数组} 
}

编译以上代码,运行结果如下所示:

整型数组元素为:
1 2 3 4 5 双精度型数组元素为:
1.1 2.2 3.3 4.4 字符型数组元素为:
H E L L O 

在这段代码中,虽然我们使用了泛型方法printArray来输出不同类型的数组元素,但实际上并没有创建一个泛型数组。我们只是在使用已有的数组来进行迭代遍历并输出元素,而不是通过泛型参数创建新的数组。


在Java中,直接创建一个泛型数组是不允许的。例如,以下代码是不合法的:

// 这是不合法的,无法直接创建泛型数组
T[] array = new T[10];

​ 泛型数组的限制是为了确保类型安全性。虽然在代码中使用了泛型方法,但我们并没有在代码中直接创建泛型数组,而是使用了已有的数组。因此,虽然代码中有泛型方法,但没有涉及创建泛型数组的情况。


通配符和边界

泛型还支持通配符和边界的概念,让你能够更精细地控制泛型类型的范围。通配符?表示未知类型,而边界则限制了允许传递的类型范围。

import java.util.List;public class GenericWildcard {/*** 打印通配符类型的列表元素** @param list 要打印的列表*/public static void printList(List<?> list) {for (Object element : list) {System.out.print(element + " ");}System.out.println();}/*** 计算数字类型列表的总和** @param list 要计算总和的列表* @return 列表元素的总和*/public static <T extends Number> double sumOfList(List<T> list) {double sum = 0;for (T element : list) {sum += element.doubleValue();}return sum;}//<T extends Number>: 这是泛型类型参数的声明。在这里,<T> 表示泛型类型参数的名称,你可以在方法体内使用它来代表实际的数据类型。extends Number 是一个泛型类型的限定符。它的意思是,类型参数 T 必须是 Number 类或其子类。这样做的目的是为了确保列表中的元素都是数字类型,以便在方法中进行数字运算。public static void main(String[] args) {// 创建一个包含不同类型元素的列表List<?> wildcardList = List.of("Hello", 42, 3.14, 'A');// 打印通配符类型的列表元素System.out.print("通配符列表元素:: ");printList(wildcardList);// 创建一个数字类型的列表List<Integer> integerList = List.of(1, 2, 3, 4, 5);// 计算数字类型列表的总和double sum = sumOfList(integerList);// 输出计算结果System.out.println("整数列表总和: " + sum);}
}

​ 输出结果如下:

通配符列表元素: Hello 42 3.14 A 
整数列表总和: 15.0

​ 上面的示例演示了一个使用通配符的方法printList和一个使用边界的方法sumOfList。通配符使方法能够接受不同类型的列表,而边界则限制了方法只能接受继承自Number的类型列表。

泛型与集合框架

Java的集合框架(如List、Set、Map等)广泛使用了泛型,使得集合中的元素类型可以更加明确和安全。使用泛型可以在编译时捕获类型错误,避免在运行时出现类型转换异常。


最后总结:

​ Java泛型是一项强大的功能,它既保持了类型安全,又提高了代码的灵活性和重用性。通过学习泛型的使用,能够编写更强大、更安全的代码,让我们的程序更加健壮和可维护。

总结一下泛型类的优缺点:

泛型类的优点:

  1. 类型安全性: 泛型类在编译时可以检查数据类型,从而提前发现类型错误,避免了在运行时出现类型转换异常。
  2. 代码重用性: 泛型类允许你创建通用的数据结构,适用于多种数据类型,减少了代码的冗余,提高了代码的重用性。
  3. 灵活性: 泛型类使得你可以创建适用于不同数据类型的通用类,从而在不同场景下实现灵活的数据存储和处理。
  4. 编译时类型检查: 泛型类使得编译器能够检查和强制确保类型的正确性,从而提高了代码的可靠性和稳定性。
  5. 更好的可读性: 使用泛型类可以提供更清晰的代码结构和命名,使得代码更易于理解和维护。

泛型类的缺点:

  1. 类型参数限制: 泛型类无法直接使用基本数据类型作为类型参数,需要使用对应的包装类。同时,无法创建泛型数组。
  2. 复杂性: 对于初学者来说,泛型的概念可能会有一定的复杂性,需要一些时间来理解和掌握。
  3. 编译时错误信息: 有时,编译器产生的泛型相关的错误信息可能不够直观,需要花时间来解读和修复。
  4. 擦除和类型擦除: 泛型类在编译时会进行类型擦除,这可能会导致在某些情况下失去一些类型信息。
  5. 部分场景不适用: 在某些特定的场景下,泛型类可能不适用,需要根据实际需求来选择是否使用泛型。

​ 尽管泛型类存在一些缺点,但它的优点远远超过了缺点,可以帮助我们写出更安全、更灵活、更高效的代码。

作者:Stevedash

发表于:2023年8月8日15点19分

来源:Java 泛型 | 菜鸟教程 (runoob.com)

注:本文内容基于个人学习理解,如有错误或疏漏,欢迎指正。感谢阅读!如果觉得有帮助,请点赞和分享。

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

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

相关文章

ZZULIOJ 1191: 数星星(结构体专题),Java

ZZULIOJ 1191: 数星星&#xff08;结构体专题&#xff09;&#xff0c;Java 题目描述 一天&#xff0c;小明坐在院子里数星星&#xff0c;Gardon就出了个难题给她&#xff1a;Gardon在天空画出了一个矩形区域&#xff0c;让他输入矩形区域里有多少颗星星&#xff0c;仁慈的上…

如何简单的无人直播

环境搭建 ffmpeg安装&#xff0c;我这里用的是centos搭建的&#xff0c;其他平台可以自己百度 yum -y install wgetwget --no-check-certificate https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-4.0.3-64bit-static.tar.xztar -xJf ffmpeg-4.0.3-64bit-static.ta…

袋鼠云数栈 DataOps 数据生产力实践,实现数据流程的自动化和规范化

袋鼠云产品团队在帮助企业进行数字化转型实践的过程中&#xff0c;发现很多企业在数据生产链路上都有着相同的问题。包括数据团队聚焦于业务需求短期内的快速交付&#xff0c;内部缺少自顶向下的数据生产管理制度&#xff0c;在数据标准、数据生产流程到研发规范的各个层面均存…

博为软件远程医疗,助力上级医生给基层病患实时医疗服务

远程医疗解决方案正逐渐成为现代医疗领域的一项重要工具。通过使用远程音视频技术&#xff0c;医生能够在不同的场景下为病患提供及时和高效的医疗服务。 这里例举博为软件远程医疗解决方案中几个常见场景&#xff1a; 1、远程会诊 远程会诊成为医生们互相交流和合作的重要方式…

【网络】数据链路层

目录 一、以太网 二、以太网帧格式 三、 MTU 1、MTU概念 2、 MTU对IP协议的影响 3、MTU对UDP协议的影响 4、 MTU对于TCP协议的影响 四、MAC地址 五、 ARP协议 1、ARP协议的作用 2、ARP协议的工作流程 3、ARP数据报的格式 4、中间人 数据链路层解决的&#xff0c;是…

分享windwosServer2012R--ISO镜像下载地址(含激活教程)

windowsServer2012R----急速网盘下载地址&#xff1a;点击下载 提取码&#xff1a;888999 激活下载&#xff1a;点击下载 提取码&#xff1a;888999

没有synchronized,rust怎么防并发?

学过Java的同学对synchronized肯定不陌生&#xff0c;那么rust里怎么办呢&#xff1f; 在Rust中&#xff0c;可以使用标准库提供的 std::sync::Mutex 来实现加锁功能。Mutex是互斥锁的一种实现&#xff0c;用于保护共享数据在并发访问时的安全性。 下面是一个简单的示例代码&a…

【Linux】网络层、数据链路层、DNS、ICMP协议、NAT技术

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录 &#x1f449;网络层&a…

【香瓜说职场】如何高效地提问(2018.05.06)

一、什么是低效地提问&#xff1f; 香瓜先举3个非常非常常见的低效提问实例&#xff1a; 1、“为什么我的XXX不成功&#xff1f;” 这个问题就像“为什么我会摔倒”&#xff0c;可能原因有“腿残疾”、“路上有坑”、“眼神不好”等无数种原因……“不摔倒”的我是回答不了的、…

一道Java继承与重写的经典问题

话不多说&#xff0c;先上代码 class Base {private String name "base";public Base() {tellName();printName();}public void tellName() {System.out.println("Base tell name: " name);}public void printName() {System.out.println("Base pr…

android nv21数据用mediacodec编解码

在 Android 中使用 MediaCodec 进行 NV21 编码和解码的过程如下&#xff1a; 编码 NV21 数据&#xff1a; // 创建 MediaCodec 编码器&#xff0c;并配置编码器格式和参数 val encoder MediaCodec.createEncoderByType("video/avc") val mediaFormat MediaFormat…

Django入门 - 路由Route的基本使用

文章目录 1. 直接访问视图函数&#xff0c;没有使用子路由2. 使用子路由 urls.py 我们一般叫它根路由 1. 直接访问视图函数&#xff0c;没有使用子路由 MyDjangoPro2\views.py 代码 from django.shortcuts import renderfrom django.http import HttpResponse# 视图函数Views …

消息队列 (9)-消费者核心类的实现

目录 前言消费者类设计思路核心API总体代码 前言 我们上一篇博客,写了虚拟主机的实现, 在虚拟主机中需要用到俩个未实现的类,分别是验证绑定关键字和消费者类,接下来我们实现消费者类的核心代码 消费者类设计思路 在这个类中,首先我们要持有virtualHost对象来操作数据, 然后…

【BASH】回顾与知识点梳理(十)

【BASH】回顾与知识点梳理 十 十. 文件的格式化与相关处理10.1 格式化打印&#xff1a; printf10.2 awk&#xff1a;好用的数据处理工具awk 的逻辑运算字符 10.3 文件比对工具diffcmppatch 10.4 文件打印准备&#xff1a; pr 该系列目录 --> 【BASH】回顾与知识点梳理&#…

移动端开发基础总结

移动端学习总结 (适合于复习) 移动端基础 技术选型&#xff1a; 单独制作移动端页面&#xff08;主流&#xff09; 流式布局&#xff08;百分比布局&#xff09;flex弹性布局&#xff08;强烈推荐&#xff09;lessrem媒体查询布局混合布局 响应式页面兼容移动端&#xff08;…

postman----传参格式(json格式、表单格式)

本文主要讲解postman使用post请求方法的2中传参方式&#xff1a;json格式、表单格式 首先了解下&#xff0c;postman进行接口测试&#xff0c;必须条件是&#xff1a; ♥请求地址 ♥请求协议 ♥请求方式 ♥请求头 ♥参数 json格式 先看一下接口文档&#xff0c;根据接口文档&…

深度学习环境安装依赖时常见错误解决

1.pydantic 安装pydantic时报以下错误&#xff1a; ImportError: cannot import name Annotated from pydantic.typing (C:\Users\duole\anaconda3\envs\vrh\lib\site-packages\pydantic\typing.py) 这个是版本错误&#xff0c;删除装好的版本&#xff0c;重新指定版本安装就…

项目规范 编写规范(范例)

项目目录 目录接口参考 项目目录结构设计&#xff0c;增加部分领域模型后缀强制定义&#xff0c;方便统一编码风格。 controller&#xff1a;请求处理 RestController module&#xff1a;按大业务区分&#xff0c;对多个业务对象数据聚合处理 Component manager&#xff1a;…

web-xss

一、简介 XSS 又称CSS(Cross Site Scripting)或跨站脚本攻击&#xff0c;攻击者在网页中插入由JavaScript编写的恶意代码&#xff0c;当用户浏览被嵌入恶意代码的网页时&#xff0c;恶意代码将会在用户的浏览器上执行。 二、xss的攻击方式 Dom&#xff1a;这是一种将任意 Jav…

zookeeper入门学习

zookeeper入门学习 zookeeper应用场景 分布式协调组件 客户端第一次请求发给服务器2&#xff0c;将flag值修改为false&#xff0c;第二次请求被负载均衡到服务器1&#xff0c;访问到的flag也会是false 一旦有节点发生改变&#xff0c;就会通知所有监听方改变自己的值&#…