一文通俗理解为什么需要泛型以及泛型的使用

为什么需要泛型?

public static void main(String[] args) {ArrayList list = new ArrayList();// 由于集合没有做任何限定,任何类型都可以给其中存放list.add("abc");list.add("def");list.add(5);Iterator it = list.iterator();while (it.hasNext()) {// 需要打印每个字符串的长度,就要把迭代出来的对象转成String类型String str = (String) it.next();System.out.println(str.length());}
}

思考:上面这段代码有什么问题吗?如果有,那么出现问题的原因是什么?

编译不报错,但运行发生了报错。

因为没使用泛型,导致我们想使用的时候,一旦强转就会出现类型转换异常,而在我们工作中,其实很多时候,我们都只需要一个集合容器里面放一个类型,如果有多个类型,我们就分多个容器来存放,那么进行约束存放类型的功能,就叫泛型(Generic)语法,让你在设计API时可以指定类或方法支持泛型,这样我们使用API的时候也变得更为简洁,并得到了编译时期的语法检查

泛型的使用

打开ArrayList的源码, 会发现在定义类的时候, 在类名后面加了个,然后这个E, 在多个方法参数里面都进行了使用, 这个E到底是什么?

class ArrayList<E> {public boolean add(E e) { }public E get(int index) { }...
}class ArrayList<String> {public boolean add(String e) { }public String get(int index) { }...
}class ArrayList<Integer> {public boolean add(Integer e) { }public Integer get(int index) { }...
}

我 们 发 现 , 只 要 我 们 在 newArrayList<>()的时候,** 在<>括号里面放什么类型, 那么这个E就是什么类型**,比如我们构建的集合是String类型, 那么 E 就 是 String , 我 们 构 建 的 集 合 是Integer类型, 那么E就是Integer;

当然,这个E是可换成自定义的名字,比如说MyType。

使用泛型定义类

新建一个类,然后在<>里面写一个不存在的类型,这个类型甚至不需要我们创建类,然后为了展示,书写了该属性对应的get和set方法。

public class MyGenericClass<MyType> {
//	private E e;
//
//	public E getE() {
//		return e;
//	}
//
//	public void setE(E e) {
//		this.e = e;
//	}private MyType myType;public MyType getMyType() {return myType;}public void setMyType(MyType myType) {this.myType = myType;}}

在测试类中,我们创建了2次之前所构建的类对象,两次所传递的泛型类型是不一样的,但是它都能进行接收,并且接收完成之后,也可以通过调用set方法来进行赋值,也能通过调用get方法来进行输出该数据。

public class TestMyGenericClass {public static void main(String[] args) {//模拟构建ArrayList对象的形式来构建一个MyGenericClass类对象MyGenericClass<String> my = new MyGenericClass<String>();my.setMyType("张三");System.out.println(my.getMyType());//再来创建一次其他类型的泛型MyGenericClass<Integer> my1 = new MyGenericClass<Integer>();my1.setMyType(1001);System.out.println(my1.getMyType());}
}

使用泛型定义接口

public interface MyGenericInterface<E> {void add(E e);E getE();
}

定义了一个带泛型的接口之后,这个接口被使用的时候,可以直接在写implements的时候,就把泛型给写死了,代表定义类的时候就确定好了泛型的类型了。

public class MyImp2 implements MyGenericInterface<String>{@Overridepublic void add(String e) {// TODO Auto-generated method stub}@Overridepublic String getE() {// TODO Auto-generated method stubreturn null;}}

定义了一个带泛型的接口之后,这个接口被使用的时候,也可以在该类上仍然不把泛型的类型给确定,让它在构建该类对象的时候去确定该类型。

public class MyImp1<E> implements MyGenericInterface<E>{@Overridepublic void add(E e) {// TODO Auto-generated method stub}@Overridepublic E getE() {// TODO Auto-generated method stubreturn null;}
}

MyImp2类在使用时无需给出泛型,但MyImp1类在使用时必须指明泛型。

public class TestGenericInterface {public static void main(String[] args) {MyImp1<Integer> my1 = new MyImp1<Integer>();//随着你指定的泛型类型不一样,那么它里面的两个方法参数和返回类型也发生了变化my1.add(1001);//这个类型的创建,不需要你使用泛型了,因为我们在该类构建的时候,就已经确定了父接口的//泛型的类型,所以它的方法都跟着变了,以后就不能发生改变了!MyImp2 my2 = new MyImp2();my2.add("你好");}
}

使用泛型定义方法

在方法的返回值前面如果加上泛型的话,那么参数里面的MyType将会报错,所以,通过泛型在方法上的运用,可以让我们在书写方法重载的时候,变的更加的方便

public class MyGenericMethod {//在修饰词的位置写一个泛型的尖括号,里面随便写个类型//这样的效果同等于在类或者是接口名后加<>,只不过这样//的范围就只局限于当前的方法public <E> void show1(E e) {System.out.println(e.getClass());}public <E> E show2(E e) {return e;}public static void main(String[] args) {MyGenericMethod mm = new MyGenericMethod();mm.show1(123);mm.show1("abc");mm.show1(3.14);System.out.println(mm.show2(123));}
}

通配符

然而,如果设 Foo 是 Bar 的一个子类型(子类或者子接口),而 G 是具有泛型声明的类或接口,G并不是 G 的子类型!这一点非常值得注意,因为它与大部分人的习惯认为是不同的。

造成这个原因是因为泛型不存在继承关系

类型通配符的上下限

如果你想限制一个方法,限制其参数的泛型类型时,你有两种方式:

  1. 使用<? extends 类>来约束泛型必须要是该类或者是该类的子类

  2. 使用<? super 类>来约束泛型必须要是该类或者是该类的父类

为什么指定通配符上限的集合不能添加元素?

// 定义一个抽象类 Shape
public abstract class Shape {public abstract void draw(Canvas c);
}
// 定义 Shape 的子类 Circle
public class Circle extends Shape {// 实现画图方法,以打印字符串来模拟画图方法实现public void draw(Canvas c) {System.out.println("在画布" + c + "上画一个圆");}
}
// 定义 Shape 的子类 Rectangle
public class Rectangle extends Shape {// 实现画图方法,以打印字符串来模拟画图方法实现public void draw(Canvas c) {System.out.println("把一个矩形画在画布" + c + "上");}
}
public void addRectangle(List<? extends Shape> shapes) {// 下面代码引起编译错误shapes.add(0, new Rectangle());
}

《java疯狂讲义》给出的答案是:

与使用普通通配符相似的是,shapes.add() 的第二个参数类型是? extends Shape,它表示 Shape 未知的子类,程序无法确定这个类型是什么,所以无法将任何对象添加到这种集合中。

简而言之,这种指定通配符上限的集合,只能从集合中取元素(取出的元素总是上限的类型或其子类),不能向集合中添加元素(因为编译器没法确定集合元素实际是哪种子类型)

我的更通俗理解:List<? extends Shape> shapes传进来的是一个类别Shape本身或者子类(这里我们假设有子类A和B,且A与B之间不存在继承关系)的集合,如果shapes添加子类A的对象,那万一shapes运行时传进来的是子类B的集合,那么"shapes.add(0, new Rectangle());"这行代码是存在问题的;同理shapes添加Shape的对象也是如此。

因此通配符上限的集合,即<? extends Shape>是无法添加的元素,而通配符下限的集合,即<? super Shape>确可以。因为<? super Shape>传进来的都是Shape的父类,而我们添加的元素只要是Shape类别或者Shape的子类就是合法的。

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

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

相关文章

HtmlRAG:RAG系统中,HTML比纯文本效果更好

HtmlRAG 方法通过使用 HTML 而不是纯文本来增强 RAG 系统中的知识表示能力。通过 HTML 清洗和两步块树修剪方法&#xff0c;在保持关键信息的同时缩短了 HTML 文档的长度。这种方法优于现有基于纯文本的RAG的性能。 方法 其实主要看下围绕html提纯思路&#xff0c;将提纯后的…

KEPServerEX 中信道深入介绍

以下是 KEPServerEX 中信道&#xff08;Channel&#xff09; 的详细介绍&#xff0c;涵盖其定义、功能、配置步骤及最佳实践&#xff0c;帮助您快速掌握信道在数据采集中的核心作用&#xff1a; 一、信道&#xff08;Channel&#xff09;的定义 信道 是 KEPServerEX 中 连接物…

C#(Winform)通过添加AForge添加并使用系统摄像机

先展示效果 AForge介绍 AForge是一个专门为开发者和研究者基于C#框架设计的, 也是NET平台下的开源计算机视觉和人工智能库 它提供了许多常用的图像处理和视频处理算法、机器学习和神经网络模型&#xff0c;并且具有高效、易用、稳定等特点。 AForge主要包括: 计算机视觉与人…

迅为RK3568开发板篇OpenHarmony实操HDF驱动配置LED-LED测试

将编译好的镜像全部进行烧写&#xff0c;镜像在源码根目录 out/rk3568/packages/phone/images/目录下。 烧写完成之后&#xff0c;在调试串口查看打印日志&#xff0c;如下图所示&#xff1a; 然后打开 hdc 工具&#xff0c;运行测试程序&#xff0c;输入“led_test 1”&…

在VS2022中配置DirectX12环境,并显示显示一个窗口

1.创建空项目并配置项目&#xff1a; 1.打开VS2022,创建C项目中的空项目 2.新建一个Main.cpp文件 3.配置项目 将属性页的C/C项中的语言栏的符合模式设置为否 再将链接器中的系统栏的子系统设置为窗口 设置完成&#xff01; 2.创建一个Windows窗口&#xff1a; 代码&#…

AI前端开发:蓬勃发展的机遇与挑战

人工智能&#xff08;AI&#xff09;领域的飞速发展&#xff0c;正深刻地改变着我们的生活方式&#xff0c;也为技术人才&#xff0c;特别是AI代码生成领域的专业人士&#xff0c;带来了前所未有的机遇。而作为AI应用与用户之间桥梁的前端开发&#xff0c;其重要性更是日益凸显…

DeepSeek+即梦 做AI视频

DeepSeek做AI视频 制作流程第一步&#xff1a;DeepSeek 生成视频脚本和分镜 第二步&#xff1a;生成分镜图片绘画提示词第三步&#xff1a;生成分镜图片第四步&#xff1a;使用可灵 AI 工具&#xff0c;将生成的图片转成视频。第五步&#xff1a;剪映成短视频 DeepSeek 真的强&…

数组练习(深入理解、实践数组)

1.练习1&#xff1a;多个字符从两端移动&#xff0c;向中间汇聚 编写代码&#xff0c;演示多个字符从两端移动&#xff0c;向中间汇聚 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> int main() {//解题思路&#xff1a;//根据题意再…

学习threejs,使用HemisphereLight半球光

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.HemisphereLight 二、…

vue项目使用vite和vue-router实现history路由模式空白页以及404问题

开发项目的时候&#xff0c;我们一般都会使用路由&#xff0c;但是使用hash路由还是history路由成为了两种选择&#xff0c;因为hash路由在url中带有#号&#xff0c;history没有带#号&#xff0c;看起来更加自然美观。但是hash速度更快而且更通用&#xff0c;history需要配置很…

Fiori APP配置中的Semantic object 小bug

在配置自开发程序的Fiori Tile时&#xff0c;需要填入Semantic Object。正常来说&#xff0c;是需要通过事务代码/N/UI2/SEMOBJ来提前新建的。 但是在S4 2022中&#xff0c;似乎存在一个bug&#xff0c;即无需新建也能输入自定义的Semantic Object。 如下&#xff0c;当我们任…

芯片设计企业的IT支撑点

对于一个芯片设计企业&#xff0c;需要怎么样的IT支撑&#xff0c;这看起来并不是那么重要&#xff0c;并不影响芯片企业是否取得成功&#xff0c;但真正进入这个行业&#xff0c;你会发现&#xff0c;这里还是有一些门道的。 实际上&#xff0c;芯片设计企业对于IT的依赖很重&…

生成对抗网络入门:Mnist手写数字生成

本文为为&#x1f517;365天深度学习训练营内部文章 原作者&#xff1a;K同学啊 一 理论基础 生成对抗网络(Generative Adversarial Networks,GAN)是近年来深度学习领域的一个热点方向。 GAN并不指代某一个具体的神经网络&#xff0c;而是指一类基于博弈思想而设计的神经网络。…

22.4、Web应用漏洞分析与防护

目录 Web应用安全概述DWASP Top 10Web应用漏洞防护 - 跨站脚本攻击XSSWeb应用漏洞防护 - SQL注入Web应用漏洞防护 - 文件上传漏洞Web应用漏洞防护 - 跨站脚本攻击XSS Web应用安全概述 技术安全漏洞&#xff0c;主要是因为技术处理不当而产生的安全隐患&#xff0c;比如SQL注入…

软件的生命周期和需求

什么是软件的生命周期? 定义(描述) --> 创建 --> 使用 --> 销毁 (这一整个过程就是事物的生命周期) 生命周期 那么软件的生命周期又分为哪些呢? 一共分为十步: 可行性研究: 通过分析软件开发要求,确定软件项目的性质、目标和规模,得出可行性研究报告,如果可行性研…

深入理解DeepSeek与企业实践(二):32B多卡推理的原理、硬件散热与性能实测

前言 在《深入理解 DeepSeek 与企业实践&#xff08;一&#xff09;&#xff1a;蒸馏、部署与评测》文章中&#xff0c;我们详细介绍了深度模型的蒸馏、量化技术&#xff0c;以及 7B 模型的部署基础&#xff0c;通常单张 GPU 显存即可满足7B模型完整参数的运行需求。然而&…

Java 字符编码与解码:深入理解 Charset 类

目录 引言 一、什么是字符集&#xff08;Charset&#xff09;&#xff1f; 二、Charset 类的核心功能 1. 获取字符集实例 2. 编码与解码 示例1&#xff1a;字符串转字节数组 示例2&#xff1a;处理不同字符集的乱码问题 3. 字符集检测与支持 三、Charset 类的常用方法…

Redis7.0八种数据结构底层原理

导读 本文介绍redis应用数据结构与物理存储结构,共八种应用数据结构和 一. 内部数据结构 1. sds sds是redis自己设计的字符串结构有以下特点: jemalloc内存管理预分配冗余空间二进制安全(c原生使用\0作为结尾标识,所以无法直接存储\0)动态计数类型(根据字符串长度动态选择…

本地Deepseek-r1:7b模型集成到Google网页中对话

本地Deepseek-r1:7b网页对话 基于上一篇本地部署的Deepseek-r1:7b&#xff0c;使用黑窗口对话不方便&#xff0c;现在将本地模型通过插件集成到Google浏览器中 安装Google插件 在Chrome应用商店中搜索page assis 直接添加至Chrome 修改一下语言 RAG设置本地运行的模型&#…

【设计模式】【行为型模式】观察者模式(Observer)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f3b5; 当你的天空突…