Java的泛型特性和PECS特性

Java的泛型特性是Java SE 5引入的,它提供了编译时类型安全检测机制,这意味着程序可以在编译期间检测到类型错误,而不是在运行时。泛型的引入极大地增强了程序的类型安全性,减少了强制类型转换的需要。以下是Java泛型特性的详细解释:

1. 泛型的基本概念

泛型,即“参数化类型”,允许在类、接口和方法创建时使用类型参数。这个类型参数将在使用时被指定为具体的类型(比如IntegerString等)。这样,同一段代码就可以用于多种数据类型。

2. 泛型类和接口

泛型类是在类名后面添加类型参数声明的类。例如,ArrayList<E>是一个泛型类,其中E是元素的类型参数。创建实例时,指定具体的类型替换类型参数,例如ArrayList<String>ArrayList<Integer>

泛型接口与泛型类相似,只不过它是接口而不是类。例如,List<E>是一个泛型接口,其中E定义了列表元素的类型。

3. 泛型方法

泛型方法是在方法返回类型之前声明一个或多个类型参数的方法。这允许方法独立于类的任何类型参数进行参数化。例如,public static <T> void sort(List<T> list)是一个泛型方法,其中T是列表元素的类型。

4. 类型参数命名约定

虽然可以使用任何名称作为类型参数,但Java社区遵循一些常见的命名约定,如E代表集合的元素类型,KV分别代表键和值的类型,T代表“类型”(Type)的一个通用表示。

5. 泛型的类型边界

可以限制泛型类型参数必须继承自特定的类或实现特定的接口。这通过在类型参数后面添加extends关键字来实现,例如<T extends Number>限制T必须是Number或其子类。

6. 通配符

泛型的通配符是?,表示未知类型。通配符可以用来表示泛型类型的参数,如List<?>表示元素类型未知的List。它有两种形式:<? extends T><? super T>,分别代表上界(Upper Bounded)和下界(Lower Bounded)通配符。

7. 类型擦除

Java泛型是使用类型擦除来实现的,这意味着泛型信息只在编译时存在,在运行时,所有的泛型类型参数都会被擦除(替换为它们的边界或者Object),因此不存在泛型类的多个版本。

8. 泛型的限制

  • 不能实例化类型参数(如new T())。
  • 不能创建类型参数的数组(如new T[])。
  • 泛型类的静态上下文中不能使用类型参数。
  • 不能捕获泛型类型的异常。

Java的泛型特性极大地提高了代码的复用性和安全性,通过在编译时进行类型检查,减少了运行时错误的可能性。了解和熟练使用泛型,对于编写高质量的Java代码至关重要。

PECS原则

PECS原则是“Producer Extends, Consumer Super”的缩写,这是一条指导使用Java泛型时的通配符的规则,特别是在涉及到集合类时。这条规则是由Joshua Bloch提出的,旨在帮助程序员更好地利用泛型提供的灵活性和类型安全性。下面是PECS原则的详细解释:

Producer Extends

当你需要从一个数据结构获取(生产)元素时,应该使用extends通配符。这样,你可以从该数据结构读取到的类型为该通配符或其任何子类型的对象。这意味着如果你有一个List<? extends Number>,你可以从这个列表中读取Number以及任何继承自Number的类型(如IntegerDouble等)。

Consumer Super

当你需要向一个数据结构写入(消费)元素时,应该使用super通配符。这允许你写入指定的类型或其任何超类型(父类型)的对象到该数据结构中。例如,如果你有一个List<? super Integer>,你可以向这个列表中写入Integer以及任何Integer的父类型,如NumberObject

为什么使用PECS原则?

  • 类型安全:PECS原则通过限制可以从集合中读取或写入的元素的类型,提供了类型安全性。这有助于避免运行时类型错误。
  • 灵活性:使用PECS原则可以使代码更加灵活,因为它允许方法接受更广泛的参数类型。例如,一个使用了extends的方法可以处理任何符合条件的子类型,而不仅仅是一个特定的类型。
  • 兼容性:它有助于保持代码对未来的修改是兼容的,因为添加新的子类型或父类型不会破坏现有的代码。

实例应用

示例 1: 使用extends进行读取操作(Producer Extends)

假设我们有一个动物园应用,我们想编写一个方法来打印出任何动物列表中所有动物的名称。这是一个典型的生产者场景,因为我们从集合中生产(获取)元素,而不是往集合中消费(添加)元素。

public class Zoo {static class Animal {String name;public Animal(String name) {this.name = name;}public String getName() {return name;}}static class Bird extends Animal {public Bird(String name) {super(name);}}public static void printAnimalNames(List<? extends Animal> animals) {for (Animal animal : animals) {System.out.println(animal.getName());}}public static void main(String[] args) {List<Bird> birdList = Arrays.asList(new Bird("Parrot"), new Bird("Sparrow"));printAnimalNames(birdList);}
}

在这个例子中,printAnimalNames方法可以接受Animal的任何子类的列表,因为我们使用了extends。这正符合PECS原则中的“Producer Extends”部分。

示例 2: 使用super进行写入操作(Consumer Super)

现在假设我们要编写一个方法来向一个动物列表中添加一些默认的动物。这是一个典型的消费者场景,因为我们往集合中消费(添加)元素。

public class Zoo {static class Animal {String name;public Animal(String name) {this.name = name;}public String getName() {return name;}}static class Bird extends Animal {public Bird(String name) {super(name);}}public static void addDefaultAnimals(List<? super Bird> animals) {animals.add(new Bird("Default Parrot"));animals.add(new Bird("Default Sparrow"));}public static void main(String[] args) {List<Animal> animalList = new ArrayList<>();addDefaultAnimals(animalList);// Assume a method to print animal names exists// printAnimalNames(animalList);}
}

在这个例子中,addDefaultAnimals方法可以接受Bird或其任何父类的列表,因为我们使用了super。这正符合PECS原则中的“Consumer Super”部分。通过这种方式,我们可以向List<Animal>List<Object>中添加Bird对象。

PECS原则通过正确使用extendssuper通配符,提供了一种强大的方式来增加泛型代码的灵活性和类型安全性。通过上述例子,我们可以看到,遵循PECS原则可以使我们的代码更加灵活且易于维护。

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

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

相关文章

【ACW 服务端】k8s部署

k8s部署 --- apiVersion: apps/v1 kind: Deployment metadata:annotations:k8s.kuboard.cn/displayName: 【wu-smart-acw-server】后台服务端labels:k8s.kuboard.cn/layer: svck8s.kuboard.cn/name: wu-smart-acw-servername: wu-smart-acw-servernamespace: defaultresourceV…

python 处理B站视频数据,数据存本地

python 处理B站视频数据&#xff0c;数据存本地 绪论实现1 导入头文件2. 获得数据的函数3 获取dvid4 获取数据&#xff0c;保存 后续 绪论 上一个已经通过B站的API&#xff0c;来实现了对数据的读取&#xff0c;这篇文章就是&#xff0c;将数据存储在本地的TXT文件中 上一篇文…

记autodl跑模型GPU CPU利用率骤变为0问题

目录 问题 解决 问题 实验室服务器资源紧张&#xff0c;博主就自己在autodl上租卡跑了&#xff0c;autodl有一个网络共享存储&#xff0c;可挂载至同一地区的不同实例中&#xff0c;当我们在该地区创建实例开机后&#xff0c;将会挂载文件存储至实例的/root/autodl-fs目录…

Grafana 安装指南

目录 介绍 安装 卸载 Grafana 汉化 介绍 Grafana是一款开源数据可视化平台&#xff0c;支持连接多种数据源&#xff0c;创建定制化仪表盘&#xff0c;通过直观的查询编辑器分析数据&#xff0c;设置灵活的警报规则并接收通知&#xff0c;具备丰富的插件生态系统&#xff0c…

韩国量子之梦:将量子计算纳入新增长 4.0战略

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 编辑丨王珩 编译/排版丨沛贤 深度好文&#xff1a;1500字丨9分钟阅读 据《朝鲜邮报》报道&#xff0c;韩国将推出由量子计算加速的云服务&#xff0c;并在首尔地区启动城市空中交通的试飞&…

微信小程序订阅消息前后端示例

微信小程序的订阅消息&#xff0c; 必须是由弹框&#xff0c;弹框&#xff0c;弹框来调起了&#xff0c;单纯的在页面上调用 wx.requestSubscribeMessage是没有效果的 小程序端的代码 <view class"sub" bindtap"dinyuxiaoxi">订阅消息</view>…

Leetcoder Day27| 贪心算法part01

语言&#xff1a;Java/Go 理论 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 什么时候用贪心&#xff1f;可以用局部最优退出全局最优&#xff0c;并且想不到反例到情况 贪心的一般解题步骤 将问题分解为若干个子问题找出适合的贪心策略求解每一个子…

【Linux系统化学习】信号概念和信号的产生

目录 信号的概念 从生活中的例子中感知信号 前台进程和后台进程 前台进程 后台进程 操作系统如何知道用户向键盘写入数据了&#xff1f; 进程如何得知自己收到了信号&#xff1f; 信号捕捉 signal函数 Core Dump&#xff08;核心转储&#xff09; 信号产生的方式 通…

黑马c++ STL部分 笔记(2) string容器

char*是指针 string是类&#xff0c;类内部封装了char*&#xff0c;管理这个字符串&#xff0c;是一个char*型的容器 函数&#xff1a;find,copy,delete,replace,insert等 1.构造string // string的构造方式&#xff1a; // 1 string() 创建一个空字符串 string str // 2 s…

LeetCode 刷题 [C++] 第102题.二叉树的层序遍历

题目描述 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 题目分析 题目中要求层序遍历二叉树&#xff0c;即二叉树的广度优先搜索(BFS)。BFS一般使用队列的先入先出特性实现&#…

react倒计时功能

目录 类组件写法 函数组件写法&#xff1a; demo: 手机获取验证码登录&#xff08;验证码60秒倒计时&#xff09; react倒计时5 秒 React中的倒计时可以通过使用setInterval()函数来实现。下面是一个示例代码&#xff1a; 类组件写法 import React from react; import { But…

【Docker】狂神说

图片后补 官网&#xff1a; https://www.docker.com/ Docker概述 Docker为什么出现 原因&#xff1a;环境配置不能跨平台 方案 传统方式&#xff1a;jar&#xff08;开发人员&#xff09; 部署&#xff08;运维人员&#xff09; 解决方式&#xff1a;开发打包上线一套流程 …

推荐几个css+js特效网站

CodePen&#xff08;https://codepen.io/&#xff09;&#xff1a;CodePen不仅仅是一个CSS编辑器&#xff0c;它也是一个广受欢迎的JavaScript特效展示平台。你可以在CodePen上浏览和搜索其他开发者创建的各种JavaScript特效&#xff0c;从而获得灵感和学习如何实现不同的效果。…

【算法集训】基础算法:基础排序 - 选择排序

从上一学期期末考完之后就鸽了&#xff0c;真的惭愧&#xff0c;还想着期末考完就开始呢&#xff0c;结果考驾照完了后又有学校项目要忙活。终于是拖到开学了哈哈&#xff0c;虽然现在事情也比较多&#xff0c;但是总归是要开始的&#xff0c;不然马上就要拖延症晚期了&#xf…

设计模式:结构型模式

1. 适配器模式 (Adapter Pattern) 适配器模式是一种结构型设计模式&#xff0c;旨在将一个类的接口转换成客户端所期待的另一个接口&#xff0c;从而使原本由于接口不兼容而无法一起工作的类能够协同工作。适配器模式通常用于需要复用现有类但其接口与要求不匹配的情况。 1.1…

Qt SQLite的创建和使用

重点&#xff1a; 1.SQLite创建数据库内容方法 链接&#xff1a;SQLite Expert Personal的简单使用-CSDN博客 2.和数据库进行链接方法 QSqlDatabase DB; //数据库连接bool MainWindow::openDatabase(QString aFile) {DBQSqlDatabase::addDatabase("QSQLITE"); /…

使用uniapp开发时自定义tabbar

预览图&#xff1a; 一、配置page.jsong中的tabbar&#xff08;这一步是必须的&#xff0c;因为我们在使用uni.switchTab()时必须要用到&#xff09; "tabBar": {"list": [{"pagePath": "pages/index/index","iconPath": &…

java回顾总结--代理模式

目录 一、代理模式1.1 静态代理示例 1.2 动态代理示例 二、总结 一、代理模式 1.1 静态代理 代理模式给某一个对象提供一个代理对象&#xff0c;并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。比如你按照小卡片上的电话打过去寻求服务&#…

C# 学习第四弹——字符串

一、char类型的使用 字符使用单引号&#xff0c;单个字符 转义字符是一种特殊的字符变量&#xff0c;以反斜线开头&#xff0c;后跟一个或多个字符。 输出多级目录可以使用 二、字符串的声明和初始化 1、引用字符串常量 引用字符串常量初始化——字符使用单引号&#xff0…

加密与安全_探索常用编码算法

文章目录 概述什么是编码编码分类ASCII码 &#xff08;最多只能有128个字符&#xff09;Unicode &#xff08;用于表示世界上几乎所有的文字和符号&#xff09;URL编码 &#xff08;解决服务器只能识别ASCII字符的问题&#xff09;实现&#xff1a;编码_URLEncoder实现&#xf…