疯狂Java讲义_08_泛型

news2024/9/21 22:35:21

文章目录

    • 泛型的传参
      • 若函数里的参数使用基类接受所有的派生类,怎么做?
    • 类型通配符的上限
    • 类型通配符的下限

泛型的传参

注意
若类 Base 是类 Derived 的基类(父类),那么数组类型 Base[] 是 Derived[] 的基类(父类)。但是集合类型 List<Base> 不是 List<Derived> 的基类(父类)。

若函数里的参数使用基类接受所有的派生类,怎么做?

例如:函数里的参数要接受所有的List类,包括 List<Integer>, List<String> 等。

public class Test01 {
	
	public static void main(String[] args) {

		ArrayList<String> strList = new ArrayList<String>();
		strList.add("good");
		strList.add("man");
		strList.add("helo");
		testGenericParameter(strList);
	}
// 形参类型 可以使用  List (会有警告),List<?> (推荐,没有警告), 不可以使用 List<Object>
	private static void testGenericParameter(List<?> list) {
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
	}
}	

例如:Shape 是基类,Circle、Rectangle 是派生类
只想要接受所有Shape派生类的入参

//  ? extends Shape   表示 所有从Shape派生出的类
void testGenericParameter(List<? extends Shape> list) {

}
  • ? extends Shape 表示 所有 Shape 的派生类
  • ? super Shape 表示所有 Shape 的基类(父类)

类型通配符的上限

List<? extends Shape> 是受限制通配符的例子,此处的问号(? )代表一个未知的类型,就像前面看到的通配符一样。但是此处的这个未知类型一定是Shape的子类型(也可以是Shape本身),因此可以把Shape称为这个通配符的上限(upper bound)。
类似地,由于程序无法确定这个受限制的通配符的具体类型,所以不能把Shape对象或其子类的对象加入这个泛型集合中。例如,下面代码就是错误的
在这里插入图片描述

与使用普通通配符相似的是,shapes.add()的第二个参数类型是 ? extends Shape,它表示Shape未知的子类,程序无法确定这个类型是什么,所以无法将任何对象添加到这种集合中。
简而言之,这种指定通配符上限的集合,只能从集合中取元素(取出的元素总是上限的类型或其子类),不能向集合中添加元素(因为编译器没法确定集合元素实际是哪种子类型)。
对于更广泛的泛型类来说,指定通配符上限就是为了支持类型型变。比如Foo是Bar的子类,这样A<Foo> 就相当于A<? extends Bar> 的子类,可以将A<Foo> 赋值给A<? extends Bar> 类型的变量,这种型变方式被称为协变

对于协变的泛型而言,它只能调用泛型类型作为返回值类型的方法(编译器会将该方法返回值当成通配符上限的类型);而不能调用泛型类型作为参数的方法。 口诀是:协变只出不进!

提示:没有指定通配符上限的泛型类,相当于通配符上限是Object 。例如 List<?> 表示 类型上限是Object

类型通配符的下限

除可以指定通配符的上限之外,Java也允许指定通配符的下限,通配符的下限用<? super 类型> 的方式来指定,通配符下限的作用与通配符上限的作用恰好相反。
指定通配符的下限就是为了支持类型型变。比如Foo是Bar的子类,当程序需要一个A<? super Foo> 变量时,程序可以将A<Bar> 、A<Object> 赋值给A<? super Foo> 类型的变量,这种型变方式被称为逆变
对于逆变的泛型集合来说,编译器只知道集合元素是下限的父类型,但具体是哪种父类型则不确定。因此,这种逆变的泛型集合能向其中添加元素(因为实际赋值的集合元素总是逆变声明的父类),从集合中取元素时只能被当成Object类型处理(编译器无法确定取出的到底是哪个父类的对象)。

对于逆变的泛型而言,它只能调用泛型类型作为参数的方法;而不能调用泛型类型作为返回值类型的方法。 口诀是:逆变只进不出!

设自己实现一个工具方法:实现将src集合中的元素复制到dest集合的功能,因为dest集合可以保存src集合中的所有元素,所以dest集合元素的类型应该是src集合元素类型的父类。
对于上面的copy()方法,可以这样理解两个集合参数之间的依赖关系:不管src集合元素的类型是什么,只要dest集合元素的类型与前者相同或者是前者的父类即可,此时通配符的下限就有了用武之地。
下面程序采用通配符下限的方式来实现该copy()方法。

public class TestGenericType {
	public static void main(String[] args) {
		ArrayList<Number> list1 = new ArrayList<Number>();
		ArrayList<Integer> list2 = new ArrayList<Integer>();
		list2.add(1);
		list2.add(2);
		list2.add(3);
		Integer last = copy(list1, list2);//①
		System.out.println(list1);
	}
	
	// dest 集合里的元素的类型必须是 src 集合元素的类型 或 其父类
	public static <T> T copy(Collection<? super T> dest, Collection<T> src) {
		T last = null;
		for (T ele : src) {
			last = ele;
			//逆变的泛型集合添加元素是安全的
			dest.add(ele);
		}
		return last;
	}
}

使用这种语句,就可以保证程序的①处调用后推断出最后一个被复制的元素类型是Integer,而不是笼统的Number类型。

实际上,Java集合框架中的TreeSet< E> 有一个构造器也用到了这种设定通配符下限的语法,如下所示。

    public TreeSet(Comparator<? super E> comparator) {
        //...
    }

正如前一章所介绍的,TreeSet会对集合中的元素按自然顺序或定制顺序进行排序。如果需要TreeSet对集合中的所有元素进行定制排序,则要求TreeSet对象有一个与之关联的Comparator对象。上面构造器中的参数comparator就是进行定制排序的Comparator对象。
Comparator接口也是一个带泛型声明的接口

public interface Comparator<T> {
    int compare(T o1, T o2);
}

通过这种带下限的通配符的语法,可以在创建TreeSet对象时灵活地选择合适的Comparator。
假定需要创建一个TreeSet<String> 集合,并传入一个可以比较String大小的Comparator,这个Comparator既可以是Comparator<String> ,也可以是Comparator<Object> 只要尖括号里传入的类型是String的父类型(或它本身)即可。

	private static void test02() {
		// 既可以使用 new Comparator<Object> 因为 Object 是 String 的父类,也可以使用 Comparator<String> 作为构造器的参数
		TreeSet<String> set1 = new TreeSet<String>(new Comparator<Object>() {
			@Override
			public int compare(Object o1, Object o2) {
				return o1.hashCode() > o2.hashCode() ? 1 :
					o1.hashCode() < o2.hashCode() ? -1 : 0;
			}
		});
		set1.add("hello");
		set1.add("wa");
		TreeSet<String> set2 = new TreeSet<String>(new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				return o1.length() > o2.length() ? -1 :
					o1.length() < o2.length() ? 1 : 0;
			}
		});
		set2.add("hello");
		set2.add("wa");
		System.out.println(set1);
		System.out.println(set2);
	}

通过使用这种通配符下限的方式来定义TreeSet构造器的参数,就可以将所有可用的Comparator作为参数传入,从而增加了程序的活性。当然,不仅TreeSet有这种用法,TreeMap也有类似的用法,具体的请查阅Java的API文档。

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

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

相关文章

【编码解码神器】CyberChef v10.18.9

# 简介 CyberChef 是一个在线编码解码工具&#xff0c;包含了四百多种在线编解码工具&#xff0c;它提供了一种简单易用的方式来对数据进行各种加密、解密、编码和解码操作。你可以把它想象成一个多功能的”数字厨房”&#xff0c;在这里&#xff0c;你可以用各种”烹饪”方法…

无人机与自主系统

无人机&#xff08;Unmanned Aerial Vehicle, UAV&#xff09;和自主系统正在迅速改变许多行业&#xff0c;从农业到物流&#xff0c;再到军事领域。无人机作为一种能够自主或半自主飞行的飞行器&#xff0c;结合自主系统的技术&#xff0c;具备了更高的灵活性和执行复杂任务的…

牛羊肉巨头的数字化战略:凯宇星辉如何领先市场

凯宇星辉的创业成长史&#xff0c;给出了中国牛羊肉企业如何从散户走向集团化经营的路线图。 总部位于大连的凯宇星辉&#xff0c;在牛羊肉进口贸易领域白手起家&#xff0c;十余年时间&#xff0c;已形成以澳新、南美、北美等全球三大牛羊肉主产区为主渠道的全球直采网络布局…

【linux|001】Unix和Linux的关系 及 它们的发展历史

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 ​ &#x1f3c5;阿里云ACE认证高级工程师 ​ &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社…

巴黎奥运会上,墨水屏标签能怎么玩?

截至8月7日&#xff0c;中国代表团在2024巴黎奥运会上已经斩获了22金21银16铜&#xff0c;合计59枚奖牌&#xff0c;位居奖牌第二。在为奥运健儿欢呼喝彩的同时&#xff0c;我们也注意到巴黎奥运会在环保方面的创新&#xff0c;并探讨如何应用墨水屏标签这一智慧显示技术&#…

[转]通俗易懂的LLM(上篇)

前言 2022年年底OpenAI发布ChatGPT&#xff0c;将LLM&#xff08;Large Language Model&#xff09;带向了一个新的高度&#xff0c;而2023年OpenAI继续放出大招&#xff1a;更强大的GPT-4问世&#xff0c;瞬间引爆了整个互联网圈。在这个大模型时代&#xff0c;作为一名NLPer&…

什么是oled?

LED 是有机发光二极管&#xff08;Organic Light-Emitting Diode&#xff09;的缩写&#xff0c;是一种先进的显示技术。与传统的液晶显示技术&#xff08;LCD&#xff09;不同&#xff0c;OLED 显示器不需要背光模块&#xff0c;因为每个像素本身可以发光。 OLED 的基本原理和…

文档控件DevExpress Office File API v24.1 - 支持基于Unix系统的打印

DevExpress Office File API是一个专为C#, VB.NET 和 ASP.NET等开发人员提供的非可视化.NET库。有了这个库&#xff0c;不用安装Microsoft Office&#xff0c;就可以完全自动处理Excel、Word等文档。开发人员使用一个非常易于操作的API就可以生成XLS, XLSx, DOC, DOCx, RTF, CS…

Java实战二 添加lombok使用@Data,编写第一个接口-用户注册并使用postman测试

添加lombok依赖 使用Data注解&#xff0c;省略写getter和setter 创建返回结果Result类 创建三层结构UserController类UserService接口UserServiceImpl实现类UserMapper接口 UserController编写注册接口register UserService定义方法 UserServiceImpl实现方法 UserMapper中编写s…

一款免费、简单、快速的JS打印插件,web 打印组件,基于JavaScript开发,支持数据分组,快速分页批量预览,打印,转pdf,移动端,PC端

前言 在数字化办公时代&#xff0c;打印需求呈现多样化和复杂化的趋势。现有的打印软件往往存在cao作繁琐、兼容性差、功能单一等问题&#xff0c;难以满足现代企业高效、灵活的打印需求。 为了解决这些痛点&#xff0c;一款简单、高效、多功能的打印插件成为了迫切需求。 介…

《MySQL数据库》数据导入、导出、表处理—/—<4>

一、插入数据 1、可使用外部工具navicat导入数据的情况下 因为部分公司不允许使用外部工具去导入数据 对于大批量数据&#xff0c;除了上节课中使用导入向导插入数据&#xff0c;也可在vscode中打开csv文件&#xff0c;然后选中光标&#xff0c;长按shiftctrl&#xff0c;拖动…

基于springboot的小微企业融资征信平台系统的设计与实现-计算机毕业设计源码99083

摘 要 本文详细阐述了一个基于Spring Boot框架的小微企业融资征信平台系统的设计与实现过程。该系统旨在为小微企业、金融机构以及征信机构提供一个高效、安全的融资征信交流平台。系统支持企业用户的登录注册、首页浏览、交流论坛参与、通知公告查看、新闻资讯阅读以及个人账户…

点亮童梦思考之光,神秘伙伴震撼登场!

本文由 ChatMoney团队出品 介绍说明 咱们来聊聊“十万个为什么”机器人&#xff0c;这对小朋友来说&#xff0c;好处可多了去啦&#xff01; 小朋友们天生好奇&#xff0c;满脑子都是问号。 这个机器人就像个啥都懂的知识达人&#xff0c;不管他们问啥&#xff0c;都能给出答…

数据加密-AES数据加密WPF程序

AES&#xff08;Advanced Encryption Standard&#xff09;是一种广泛使用的对称密钥加密算法&#xff0c;由美国国家标准与技术研究院&#xff08;NIST&#xff09;于2001年发布。AES以其高效、安全的特点&#xff0c;在数据加密领域占据了重要地位。 <Grid><Grid.Ro…

定期检查m000是否消耗pga

偶然发现一个库的m000占用pga较高&#xff0c;导致ora-4036问题 sho parameter ga 看看当前参数 好像是bug 需要定期检查 select to_char(sysdate,yyyy-mm-dd hh24:mi:ss)riqi from dual;select round(sum(PGA_ALLOC_MEM)/1024/1024,2) "Total PGA Allocated (Mb)&q…

数据可视化(医疗数据)

目 录 第1章 绪 论 1 1.1 课题背景及研究目的 1.2 课题研究内容 第2章 课题概要及关键技术 2.1 课题概要 2.2 数据说明 2.3 关键技术 第3章 数据分析 3.1 数据统计分析 3.2 可视化分析 第4章 数据建模 4.1 数据预处理 4.2 模型构建 第5章 模型评估与应用 5.1 模型…

App广告投放新选择:Xinstall,让数据监测变得简单又高效

在App推广的战场上&#xff0c;广告投放是夺取用户心智的重要手段。然而&#xff0c;广告主们常常面临一个难题&#xff1a;如何准确衡量广告投放的效果&#xff1f;如何挖掘出有价值的用户来源和优质的投放渠道&#xff1f;这时候&#xff0c;你就需要一款强大的广告效果监测工…

基于Hadoop的超市进货推荐系统设计与实现【springboot案例项目】

文章目录 有需要本项目的代码或文档以及全部资源&#xff0c;或者部署调试可以私信博主项目介绍系统分析系统设计数据表设计表4-1&#xff1a;关于我们表4-2&#xff1a;用户表4-3&#xff1a;管理员表表4-4&#xff1a;token表表4-5&#xff1a;系统简介表4-6&#xff1a;收藏…

大数据-72 Kafka 高级特性 稳定性-事务 (概念多枯燥) 定义、概览、组、协调器、流程、中止、失败

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

Ansys Mechanical|如何用好远程点(Remote Points)

一&#xff0e;什么是远程点&#xff1f; 当你定义载荷、约束和接触的作用域时&#xff0c;一般习惯于采用诸如直接选择模型或是通过模型的自定义截面等方法。 实际上&#xff0c;远程点也是一种设置远程边界条件作用域的方法。远程边界条件包括&#xff1a; 结构分析和温度分…