【用Java学习数据结构系列】泛型上界与通配符上界

 看到这句话的时候证明:此刻你我都在努力

加油陌生人

个人主页:Gu Gu Study
专栏:用Java学习数据结构系列
喜欢的一句话: 常常会回顾努力的自己,所以要为自己的努力留下足迹

喜欢的话可以点个赞谢谢了。
作者:小闭


目录

前言

泛型的概念

泛型的擦除机制

泛型的上界

通配符上界

我们实现一下场景一:

场景二:

泛型上界与通配符上界的区别

泛型上界:

通配符上界:

通配符下界


前言

本系列准备已经结束,反射,lambda表达示,之类知识了。本系列属于数据结构初阶,进阶的敬请期待。本文章主要是讲泛型的进一步认识,以及更加底层的String类的认识。

之前也写过一篇泛型初阶的一篇文章,大家如果没看过可以再看看。

这篇文章已经讲了:包装类,简单的编译器推导,泛型的基本使用,以及泛型上界。

泛型的概念

泛型是Java中一种强大的特性,它允许程序员在编写代码时指定类型参数,从而使得代码更加灵活和可重用。泛型提供了一种方式,使得编译器可以在编译时检查类型安全,避免了类型转换的错误和运行时的类型检查。

通俗来说: 就是适用于许多许多类型 ,从代码上讲,就是对类型实现了参数化。

语法:

class 泛型类名称<类型形参列表> {

// 这里可以使用类型参数

}

简单示例泛型的简单使用:

class MyArray<T> {  //注释1public Object[] array = new Object[10];public T getPos(int pos) {return (T)this.array[pos];}public void setVal(int pos,T val) {this.array[pos] = val;}
}
public class TestDemo {public static void main(String[] args) {MyArray<Integer> myArray = new MyArray<>();//注释2myArray.setVal(0,10);myArray.setVal(1,12);//注释3int ret = myArray.getPos(1);System.out.println(ret);myArray.setVal(2,"bit");//注释4 此处是错误的}
}

代码注释处解析:

注释一:我们在类名后加了<T>,这里的作用就是泛型得基本用法,相当于这个T就代表一个类,但具体是哪个类我,还需要在创建这个类对象的时候,我们指定哪个类,才会知道。

注释二:这里我们创建对象时(new一个对象时)我们同样在类名后加了<Integer>,这就是我们指定T就是Integer类,则在我们创建的类对象时 T 就是Integer。

注释三:因为我们指定T为Intege类型,则在使用setVal(1,12);方法时我们可以直接传参12,然后jJVM就会进行自动拆包了。

注释四:首先说明这里的使用是编译器是会报错的,因为我们前面已经指定T为Integer了,这是传入一个String显然是不对的。所以编译器是会报错的。要想传入String储存到数组中,我们就需要在创建一个MyArray<String>对象。


泛型的擦除机制

关于泛型的擦除机制,它是Java泛型实现的一个核心概念。在Java中,泛型的类型参数在编译期间会被替换为其边界或Object类型,这个过程被称为类型擦除。这意味着在运行时,泛型的类型参数实际上是被“擦除”了,泛型代码在运行时无需知晓具体的类型参数。例如,如果有一个泛型类`List<T>`,在运行时,无论`T`是什么类型,`List<T>`都会被当作`List<Object>`来处理。

这种机制的主要目的是为了向后兼容Java的旧版本,同时减少代码重复,使得代码更加简洁。但是,这也带来了一些限制和挑战,比如不能在运行时获取泛型参数的具体类型,泛型数组的创建受到限制等。

尽管如此,通过一些技巧和设计模式,可以在一定程度上绕过这些限制,让代码更加灵活和可扩展。

类型擦除机制也意味着,在编译过程中,所有的泛型类型参数`T`都会被替换为`Object`,这就是我们通常所说的泛型擦除。由于被编译器当作`Object`类型处理,我们可以通过反射set任意类型的参数。但是,这种擦除也导致了一些问题,比如在泛型类中不能直接调用泛型参数的具体方法,因为这在编译时是未知的。解决这个问题的一种方法是给泛型参数一个边界,这样编译器就能知道泛型参数至少具有哪些方法。

总结:泛型的擦除机制是Java泛型实现的关键部分,它允许泛型代码在运行时以一种类型安全的方式处理不确定的类型,但同时也带来了一些限制和挑战。


泛型的上界

什么是泛型上界呢?

泛型上界(Bounded Type Parameters)是泛型编程中的一个概念,它允许我们为泛型类型参数指定一个边界,即限制泛型参数必须是某个类或接口的子类或实现。这样做可以提供更多的类型安全,并允许在泛型代码中使用更具体的操作。

在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。 就如上面所说给泛型一个边界,这样编译器就能知道泛型参数至少具有哪些方法。

代码语法:

class 类名 <形参类型 extends 另一个类>{
//代码
}

例如:

public class Myarr< E extends Number >{
//代码
}

代码解析:

这里当我们要创建Myarr这个类对象时,指定 E 的类型时,那么这时我们指定的类只能是Number类或它的子类作为类型实参。如果不是那么编译器就会报错。

扩展:那么我们如果我们没有定义上界,而是public class Myarr< E >时,我们就可以看做E extends Object;

通配符上界

我们上面说过当我们使用泛型类时吗,我们需要指定一个类作为泛型参数类。那么这时我们就会产生两个场景。

场景一:

那么如果我们有一个方法,是获取各个泛型类对象里的元素,这时我们定义这个方法时的形参类型到底怎么确定呢?如果我们指定一个类就会使得形参类型定死,无法实现获取各种泛型类里面的元素。

场景二:

如果我们现在要创建一个泛型类,但是暂时还不想示例化,只是定义一个null的泛型类,后面才随机实例化范围内的泛型类对象。

为了实现上面两个场景,就有了通配符上界

class Food {public void show(){System.out.println("食物");}
}
class Fruit extends Food {public void show(){System.out.println("水果");}}
class Apple extends Fruit {public void show(){System.out.println("苹果");}
}
class Banana extends Fruit {public void show(){System.out.println("香蕉");}
}class Message<T> { // 设置泛型private T message ;public T getMessage() {return message;}public void setMessage(T message) {this.message = message;}
}

如上当我们有以上的类时。

我们实现一下场景一:

class TestDemo {public static void main(String[] args) {Message<? extends Fruit> message;//这时还不知道示例化苹果还是香蕉 message== new Message<Apple>() ;//过一会知道了,这时才指定类型,当然也可以是其它情况message.setMessage(new Apple());}}

如上:我们在想创建对象时还没知到message对象中 T 为什么类(对象),那么这时我们就使用 通配符上界Message<? extends Fruit>进行暂时限定泛型类的范围。到了后面我们知道了,这里的知道可以是 if 判断得出,或是返回值判断知道,并不像代码中的一样是我们后面主观指定了Apple。

场景二:

class TestDemo {public static void main(String[] args) {Message<Apple> message = new Message<>() ;message.setMessage(new Apple());fun(message);Message<Banana> message2 = new Message<>() ;message2.setMessage(new Banana());fun(message2);}// 此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,所以无法修改public static void fun(Message<? extends Fruit> temp){
//temp.setMessage(new Banana()); //仍然无法修改!
//temp.setMessage(new Apple()); //仍然无法修改!temp.getMessage().show();System.out.println(temp.getMessage());}
}

如上:我们的fun函数,我们可以根据不同的传值,就可以得出不同的对象(限定范围内的),只需要在实参里使用通配符上界的语法。

注意:因为我们这里Message的指定类型是Fruit的子类或其本身,所以这里我们可以是直接用Fruit来直接 接受这个指定的类型的,顶多也就是向上转型,但是我们是无法对temp进行设置类的,因为这时我们传入实参的时候已经确定了的,T已经确定了一个类,而我们这时也就无法对其进行设置。

简单来说: 通配符的上界,不能进行写入数据,只能进行读取数据。 这与通配符下界是完全相反的。下文还会给大家介绍通配符下界。

public static void fun(Message<? extends Fruit> temp){
//temp.setMessage(new Banana()); //仍然无法修改!
//temp.setMessage(new Apple()); //仍然无法修改!Fruit b = temp.getMessage();System.out.println(b);
}


泛型上界与通配符上界的区别

在Java中,泛型上界和通配符上界是两个不同的概念,它们在泛型编程中扮演着不同的角色。下面分别解释它们的含义和区别:

泛型上界:

泛型上界是在声明泛型类型时指定的,用来限制泛型类型参数的类型范围。

它通常用在泛型类的声明中,例如 class Box<T extends Number> 表示 T 必须是 Number 或其子类的类型。

泛型上界是静态的,即在编译时就已经确定的。

通配符上界:

通配符上界是在实例化泛型类或使用泛型方法时使用的,用来指定通配符的类型范围。

它通常用在泛型的实例化和传递参数时,例如 List<? extends Number> 表示这个列表可以包含 Number 类型及其所有子类型的元素。

通配符上界是动态的,即在运行时可以确定具体类型。

具体区别

  • 使用场景不同
    • 泛型上界是在定义泛型类或接口时使用的,用来限制类型参数的类型。
    • 通配符上界是在实例化泛型类或调用泛型方法时使用的,用来指定具体的类型范围。
  • 类型安全
    • 泛型上界提供了编译时的类型安全检查,确保类型参数不会超出指定的范围。
    • 通配符上界则提供了运行时的类型安全,允许在运行时确定具体的类型。
  • 协变与逆变
    • 泛型上界可以是协变的(extends),也可以是逆变的(super),这取决于泛型参数是用作输入还是输出。
    • 通配符上界通常是协变的,表示可以接收更具体的类型。
  • 类型擦除
    • 泛型上界在编译时会进行类型擦除,泛型类型参数会被替换为其上界。
    • 通配符上界在运行时不会进行类型擦除,它们用于保持泛型的灵活性。
  • 实例化
    • 泛型上界在定义泛型类时实例化,不需要显式指定。
    • 通配符上界在创建泛型实例时显式指定。

通俗来说:泛型上界是在定义泛型时用来限制类型参数的,而通配符上界是在实例化泛型时用来指定具体类型范围的。

通配符下界

通配符下届与上界相似,只是指定的范围有所不一样。还有就是跟上面说的与通配符的性质是相反的。

通配符的下界,只能进行写入数据,不能进行读取数据。

代码还是类似举例:

class Food {public void show(){System.out.println("食物");}
}
class Fruit extends Food {public void show(){System.out.println("水果");}}
class Apple extends Fruit {public void show(){System.out.println("苹果");}
}
class Banana extends Fruit {public void show(){System.out.println("香蕉");}
}class Plate<T> {private T plate ;public T getPlate() {return plate;}public void setPlate(T plate) {this.plate = plate;}
}

class TestDemo {public static void main(String[] args) {Plate<Fruit> plate1 = new Plate<>();plate1.setPlate(new Fruit());通配符的下界,不能进行读取数据,只能写入数据。fun(plate1);Plate<Food> plate2 = new Plate<>();plate2.setPlate(new Food());fun(plate2);}public static void fun(Plate<? super Fruit> temp) {
// 此时可以修改!!添加的是Fruit 或者Fruit的子类temp.setPlate(new Apple());//这个是Fruit的子类temp.setPlate(new Fruit());//这个是Fruit的本身
//Fruit fruit = temp.getPlate(); 不能接收,这里无法确定是哪个父类System.out.println(temp.getPlate());//只能直接输出}
}

无法接受的原因:因为在实参传入进去时T也是被确定为Fruit的父类或Fruit类,无法确定返回的是哪个父类,所以我们无法接收。

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

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

相关文章

backtrader下的轮动策略模板,附年化20.6%的策略源码。

原创内容第700篇&#xff0c;专注量化投资、个人成长与财富自由。 原创日更700天&#xff0c;回首向来萧瑟处&#xff0c;也无风雨也无晴。 但行好事&#xff0c;莫问前程&#xff0c;持续改1%&#xff0c;为社群的同学们提供价值。 今天我们实现backtrader下的轮动策略模板…

B2109 统计数字字符个数

B2109 统计数字字符个数 #include <iostream> using namespace std; # include <string.h> #include <ctype.h> #include <algorithm> int main(){ char str[256]; cin.getline(str,256); //fgets(str,256,stdin); int cnt 0; //for(size_t i 0…

使用AWS Lambda构建无服务器应用程序

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用AWS Lambda构建无服务器应用程序 AWS Lambda 简介 创建 AWS 账户 创建 Lambda 函数 配置触发器 编写和测试代码 示例代码&am…

如何在Word的表格中一次性插入多行?

当我们想在Word中的表格中一次性插入多行时&#xff0c;却发现内置的插入功能并没有插入行数选项。 此时我们只需要选择多行&#xff0c;例如&#xff0c;选择5行&#xff0c;以同样的步骤插入&#xff0c; 在下方就新增了5行。 同理&#xff0c;插入其他自定义行数。

【ACM出版,EI稳定检索,九大高校联合举办, IEEE Fellow支持】2024年计算机视觉与艺术研讨会(CVA 2024,11月29-12月1日)

大会官网&#xff1a;www.icadi.net (CVA为ICADI分会&#xff0c;网站沿用主会议&#xff1b;议程、出版将以主会为准&#xff09; 大会时间&#xff1a;2024年11月29-12月1日 大会地点&#xff1a;中国-天津 终轮截稿&#xff1a;2024年11月22号&#xff08;特殊情况联系会…

火山引擎VeDI数据服务平台:在电商场景中,如何解决API编排问题?

01 平台介绍 数据服务平台可以在保证服务高可靠性和高安全性的同时&#xff0c;为各业务线搭建数据服务统一出口&#xff0c;促进数据共享&#xff0c;为数据和应用之间建立了一座“沟通桥梁”。 同时&#xff0c;解决数据理解困难、异构、重复建设、审计运维困难等问题&#x…

网付碰一下支付系统功能分享来了!

随着碰一下支付代理骗局的持续曝光&#xff0c;越来越多的人都开始将目光转向了碰一下支付系统源码部署这一入局方式之上&#xff0c;使得这一项目愈加火爆的同时&#xff0c;也让网付等头部聚合支付公司成为了大家关注的焦点&#xff0c;连带着网付碰一下支付系统有哪些功能这…

ElasticSearch学习篇16_《检索技术核心20讲》进阶篇之空间检索

背景 学习极客实践课程《检索技术核心20讲》https://time.geekbang.org/column/article/215243&#xff0c;文档形式记录笔记。 相关问题&#xff1a; 查询范围固定的需求 直接计算两点之间距离区域二进制编码GeoHash编码 查询范围不固定的需求 GeoHash编码索引结构设计 基于…

SSH详解

一、SSH 引入 在使用 https 协议访问我们的 Gitee 库的时候&#xff0c;如果我们想要克隆一个私有库&#xff0c;或者说想要使用 git push&#xff0c;我们都需要输入账户账号和密码&#xff0c;这样十分繁琐而且很不安全。 虽然我们可以使用 Git for Windows 的 Git Credent…

互联网大厂钟爱的压测工具分享,战绩可查!

双11来了&#xff0c;又到了一些互联网大厂技术团队疯狂&#xff08;~~加班&#xff09;~~备战的紧张时刻。 今天&#xff0c;为大家带来一期“互联网大厂亲测的压测工具分享”&#xff0c;并在结尾附上30天SaaS版免费体验&#xff01; 压力测试相比于监控而言&#xff0c;是…

mac-泛洪

泛洪攻击的类型 TCP SYN Flood&#xff1a; 攻击者向目标服务器发送大量的 TCP SYN 请求&#xff0c;但不完成握手过程。服务器为每个请求分配资源&#xff0c;最终可能耗尽其连接表&#xff0c;导致无法处理正常请求。 UDP Flood&#xff1a; 攻击者向目标发送大量的 UDP 数据…

【数据分享】1901-2023年我国省市县镇四级的逐年最高气温数据(免费获取/Shp/Excel格式)

之前我们分享过1901-2023年1km分辨率逐月最高气温栅格数据和Excel和Shp格式的省市县镇四级逐月最高气温数据&#xff0c;原始的逐月最高气温栅格数据来源于彭守璋学者在国家青藏高原科学数据中心平台上分享的数据&#xff01;基于逐月数据我们采用求年平均值的方法得到逐年最高…

算法笔记:Day-09(初始动态规划)

509. 斐波那契数 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n) F(n - 1) F(n - 2)&#xff0c;其中 …

「Mac畅玩鸿蒙与硬件27」UI互动应用篇4 - 猫与灯的互动应用

本篇将带领你实现一个趣味十足的互动应用&#xff0c;用户点击按钮时猫会在一排灯之间移动&#xff0c;猫所在的位置灯会亮起&#xff08;on&#xff09;&#xff0c;其余灯会熄灭&#xff08;off&#xff09;。应用会根据用户的操作动态更新灯光状态和文本提示当前亮灯的位置&…

Python毕业设计选题:基于django+vue的4S店客户管理系统

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 员工信息管理 个人中心 车辆信息管理 售后服务管理 售后安排管理 车辆信…

优选算法精品——双指针

移动零 算法原理&#xff1a; 1.数组划分,数组分块 2.双指针算法 (利用数组下标来充当指针) 两个指针的作用: cur:从左往右扫描数组,遍历数组 dest:已处理的区间内,非零元素的最后一个位置 代码实现&#xff1a; cur 从前往后遍历的过程中: 1.遇到0元素:cur; 2.遇到 非零元…

音视频入门基础:FLV专题(22)——FFmpeg源码中,获取FLV文件音频信息的实现(中)

本文接着《音视频入门基础&#xff1a;FLV专题&#xff08;21&#xff09;——FFmpeg源码中&#xff0c;获取FLV文件音频信息的实现&#xff08;上&#xff09;》&#xff0c;继续讲解FFmpeg获取FLV文件的音频信息到底是从哪个地方获取的。本文的一级标题从“四”开始。 四、音…

一个由Deno和React驱动的静态网站生成器

大家好&#xff0c;今天给大家分享一个由 Deno React 驱动的静态网站生成器Pagic。 项目介绍 Pagic 是一个由 Deno React 驱动的静态网站生成器。它配置简单&#xff0c;支持将 md/tsx 文件渲染成静态页面&#xff0c;而且还有大量的官方或第三方主题和插件可供扩展。 核心…

已解决,部署GPTSoVITS报错‘AsyncRequest‘ object has no attribute ‘_json_response_data‘

部署GPTSoVITS过程中&#xff0c;开启一键三连进程发生&#xff0c;报错AsyncRequest object has no attribute _json_response_data 具体报错内容为 (GPTSoVITS) PS D:\Code\GPT-SoVITS-beta0706> python webui.py Running on local URL: http://0.0.0.0:9874 IMPORTANT:…

Chrome 130 版本开发者工具(DevTools)更新内容

Chrome 130 版本开发者工具&#xff08;DevTools&#xff09;更新内容 一、网络&#xff08;Network&#xff09;面板更新 1. 重新定义网络过滤器 网络面板获新增了一些过滤条件&#xff0c;这些过滤条件是根据反馈重新设计的&#xff0c;特定于类型的过滤条件保持不变&…