Java之泛型基础

泛型

1 问题引入

在前面学习集合时,我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。

观察下面代码:

public static void main(String[] args) {//没有给泛型参数传值,那么泛型默认表示为Object类型Collection c = new ArrayList();c.add("hello1");c.add("hello2");c.add("hello3");c.add(1);for(Object obj : c) {String str = (String) obj;System.out.println(str);}
}//运行结果:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

程序在运行时发生了问题**java.lang.ClassCastException**。

由于集合中什么类型的元素都可以存储。导致取出时强转引发运行时 ClassCastException。

Collection虽然可以存储各种对象,但实际上通常Collection只存储同一类型对象。例如都是存储字符串对象。因此在JDK5之后,新增了泛型(Generic)语法,让你在设计API时可以指定类或方法支持泛型,这样我们使用API的时候也变得更为简洁,并得到了编译时期的语法检查。

2 泛型概述

泛型(Generics)的概念是在JDK1.5中引入的,它的主要目的是为了解决类型安全性和代码复用的问题。

泛型是一种强大的特性,它允许我们在定义类、接口和方法时使用参数化类型

泛型基本语法为定义在<>中,例如下面案例:

//T是数据类型,但是不是确定的数据类型
//程序员在使用该类的时候,传入什么具体的类型给T,T就代表什么类型
public class MyClass<T> {private T value;public void setValue(T value) {this.value = value;}public T getValue() {return value;}
}

MyClass是一个泛型类,使用类型参数T。我们可以在创建对象时指定具体的类型,例如MyClass<Integer>MyClass<String>

泛型能够使我们编写出来通用的代码,提高代码的可读性和重用性。通过使用泛型,我们可以在类、接口和方法中使用类型参数,使得代码可以处理不同类型的数据,同时保持类型安全。

3 泛型应用

了解泛型的意思之后,接下来可以再看下之前学习过的集合中的泛型:

//Collection接口定义,其是一个泛型接口
public interface Collection<E> {//省略...boolean add(E e);
}

Collection是一个泛型接口,泛型参数是E,add方法的参数类型也是E类型。

在使用Collection接口的时候,给泛型参数指定了具体类型,那么就会防止出现类型转换异常的情况,因为这时候集合中添加的数据已经有了一个规定的类型,其他类型是添加不进来的。

例如下面案例中,我们指定了集合c只能存储String类型数据,则Integer类型的1就无法添加成功。

public static void main(String[] args) {Collection<String> c = new ArrayList<String>();c.add("hello1");c.add("hello2");c.add("hello3");//编译报错,add(E e) 已经变为 add(String e)//int类型的数据1,是添加不到集合中去的//c.add(1);for(String str : c) {System.out.println(str);}
}

可以看出,传入泛型参数后,add方法只能接收String类型的参数,其他类型的数据无法添加到集合中,同时在遍历集合的时候,也不需要我们做类型转换了,直接使用String类型变量接收就可以了,JVM会自动转换的

Collection<String> c = new ArrayList<String>();

可简写为菱形泛型形式:

Collection<String> c = new ArrayList<>();

菱形泛型(Diamond Operator)是JDK7中引入的一种语法糖,用于简化泛型的类型推断过程。

Map接口使用泛型:

//Map接口也是泛型接口
public interface Map<K,V> {//省略...V put(K key, V value);Set<Map.Entry<K, V>> entrySet();
}

案例:

public static void main(String[] args) {Map<Integer,String> map = new HashMap<>();//根据泛型类型的指定,put方法中的key只能是Integer类型,value只能是String类型map.put(1,"hello1");map.put(2,"hello2");map.put(3,"hello3");map.put(4,"hello4");//根据上面列出的源码可知,当前指定Map的泛型类型为:Map<Integer,String> map//entrySet方法返回的类型就应该是Set<Map.Entry<Integer, String>>Set<Map.Entry<Integer, String>> entrySet = map.entrySet();for(Map.Entry entry:entrySet){System.out.println(entry.getKey()+" : "+entry.getValue());}
}

4 自定义泛型

Java中泛型使用情况有三种:

  • 泛型类
  • 泛型接口
  • 泛型方法

注意:刚开始工作,我们自定义泛型类或接口的情况并不多,大家掌握定义泛型类、实例化泛型类对象的固定格式即可。

1)泛型类

如果泛型参数定义在类上面,那么这个类就是一个泛型类

泛型类定义格式:

[修饰符] class 类名<泛型类型名1,泛型类型名2,...> { 0个或多个数据成员;0个或多个构造方法;0个或多个成员方法;
}//注意:之前用确定数据类型的地方,现在使用自定义泛型类型名替代

例如:JDK中HashSet泛型类定义如下

在这里插入图片描述

泛型类实例化对象格式:

泛型类名<具体类型1,具体类型2,...> 对象名 = new 泛型类名<>(实参列表);

案例展示:

​ 定义一个泛型类Circle,包含x,y坐标和radius半径,然后进行功能测试。

基础泛型类:

package com.briup.chap08.bean;//自定义泛型类:圆
//class 类名<泛型类型1,泛型类型2,...>
//	泛型类型名字可以自行定义
public class Circle<T, E> {//原来具体数据类型的地方,使用泛型类型名替换即可private T x;private T y;private E radius;//无参构造器没有任何改变public Circle() {}//原来具体数据类型的地方,使用泛型类型名替换即可public Circle(T x, T y, E radius) {this.x = x;this.y = y;this.radius = radius;}public T getX() {return x;}public void setX(T x) {this.x = x;}public T getY() {return y;}public void setY(T y) {this.y = y;}public E getRadius() {return radius;}public void setRadius(E radius) {this.radius = radius;}@Overridepublic String toString() {return "Circle [x=" + x + ", y=" + y + ", radius=" + radius + "]";}
}

测试类:

package com.briup.chap08.test;import com.briup.chap08.bean.Circle;public class Test014_GenericsClass {public static void main(String[] args) {//实例化泛型类对象://  泛型类<具体类型1,具体类型2,...> 对象 = new 泛型类<>(实参s);//1.实例化具体类对象,2种泛型设置为Integer和Double// 注意,泛型类可以是任意引用类型Circle<Integer, Double> c1 = new Circle<>(2,3,2.5);int x = c1.getX();double r = c1.getRadius();System.out.println("x: " + x + " radius: " + r);System.out.println("------------------");//2.实例化具体类对象,2种泛型设置为Double和IntegerCircle<Double, Integer> c2 = new Circle<>(2.0,3.0,2);double x2 = c2.getX();int r2 = c2.getRadius();System.out.println("x2: " + x2 + " r2: " + r2);}
}//运行结果:
x: 2 radius: 2.5
------------------
x2: 2.0 r2: 2

2)泛型接口

如果泛型参数定义在接口上面,那么这个接口就是一个泛型接口

定义格式:

[修饰符] interface 接口名<泛型类型名1,泛型类型名2,...> { }

例如:JDK中Set泛型接口

在这里插入图片描述

在泛型接口中,我们使用T来代表某一个类型,这个类型具体是什么将来使用的时候再传参确定。

public interface Action<T> {...}public static void main(String[] args) {//创建匿名内部类Action<String> a = new Action<>() {//重写方法...};
}

泛型接口使用跟泛型类使用类似,在此不专门举例说明。

3)泛型方法

如果泛型参数定义在方法上面,那么这个方法就是一个泛型方法。

泛型方法定义格式:

[修饰符] <泛型类型名> 返回值类型 方法名(形式参数列表) { 方法具体实现;
}

泛型方法调用格式:

类对象.泛型方法(实参列表);

类名.static泛型方法(实参列表);

注意:泛型方法调用时不需要额外指定泛型类型,系统会自动识别泛型类型。

案例展示:

​ 在上述Circle泛型类中,补充泛型方法disp()和static show()并调用,验证上述格式。

基础类Circle:

​ 其他代码不变,核外补充下面2个泛型方法即可!

public class Circle<T,E> {//省略...//泛型类中定义 泛型方法public <F> void disp(F f) {System.out.println("in 泛型方法disp, f: " + f);}// 下面写法虽然不会报错,不建议大家这样写// 因为泛型方法上的 T 会和 泛型类上的T 产生歧义public static <T> void show(T t) {System.out.println("in 泛型static方法show, t: " + t);}
}

测试类:

public static void main(String[] args) {Circle<Integer,Integer> c = new Circle<>();//public <F> void disp(F f);//调用时系统自动识别泛型方法类型c.disp(1);		//Integerc.disp(2.3);	//Doublec.disp("hello");//Stringc.disp('h');	//CharacterSystem.out.println("--------------");//public static <T> void show(T t);//通过类名可以直接调用,不需要额外指定泛型类型Circle.show(2.3);Circle.show(2);Circle.show("hello");
}//运行结果:
in 泛型方法disp, f: 1
in 泛型方法disp, f: 2.3
in 泛型方法disp, f: hello
in 泛型方法disp, f: h
--------------
in 泛型static方法show, t: 2.3
in 泛型static方法show, t: 2
in 泛型static方法show, t: hello

5 注意事项

先看两种错误情况:

//编译通过
//父类型的引用,指向子类对象
Object o = new Integer(1);//编译通过
//Object[]类型兼容所有的【引用】类型数组
//arr可以指向任意 引用类型 数组对象
Object[] arr = new Integer[1];//编译失败
//注意,这个编译报错,类型不兼容
//int[] 是基本类型数组
Object[] arr = new int[1];//编译失败
//错误信息:ArrayList<Integer>无法转为ArrayList<Object>
//在编译期间,ArrayList<Integer>和ArrayList<Object>是俩个不同的类型,并且没有子父类型的关系
ArrayList<Object> list = new ArrayList<Integer>();

注意,= 号俩边的所指定的泛型类型,必须是要一样的

这里说的泛型类型,指的是<>中所指定的类型

虽然IntegerObject的子类型,但是ArrayList<Integer>ArrayList<Object>之间没有子父类型的关系,它们是两种不同的数据类型

所以:

Object o = new Integer(1);编译通过

ArrayList<Object> list = new ArrayList<Integer>();编译报错

也就是说,两种类型,如果是当做泛型的指定类型的时候,就没有多态的特点了

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

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

相关文章

视频去水印免费电脑版 pdf压缩在线免费网页版 pdf压缩在线免费 简单工具软件详细方法步骤分享

消除视频中的恼人水印&#xff0c;是许多视频编辑爱好者的常见需求。在这篇文章中&#xff0c;我们将探讨几种视频去水印的技巧&#xff0c;在数字化时代&#xff0c;视频和图片的传播越来越方便&#xff0c;但随之而来的水印问题也让人头疼。本文将为您详细介绍视频剪辑去水印…

vue环境安装

安装node.js 网址&#xff1a;https://nodejs.org/en/download/ 直接点击下载就ok 一路next&#xff0c;这里可以改一下保存路径 选第一个 安装后&#xff0c;找到node.js的安装目录&#xff0c;创建这两个文件夹 之后打开命令提示符&#xff0c;右键以管理员身份运行 将新创…

智能猫砂盆买开放式还是封闭式?四年养猫老手实用测评三个品牌!

有没有人跟我一样&#xff0c;买过封闭式的智能猫砂盆回来&#xff0c;结果猫咪不爱用&#xff0c;死活不肯进去&#xff0c;搞得智能猫砂盆白买了&#xff0c;但是平时上班太忙碌&#xff0c;真的很需要一个可以帮自己铲屎的智能猫砂盆&#xff0c;后面恶补了一下知识&#xf…

通信原理-思科实验四:静态路由项配置实验

实验四 静态路由项配置实验 一&#xff1a;实验内容 二&#xff1a;实验目的 三、实验原理 四、实验步骤 选择三个2811型号的路由器 R1、R2、R3 路由器默认只有两个快速以太网接口&#xff0c;为路由器R1和R3增加快速以太网接口模块NM-1FE-TX&#xff0c;安装后检查路由器的接…

JavaSE--基础语法--继承和多态(第三期)

一.继承 1.1我们为什么需要继承? 首先&#xff0c;Java中使用类对现实世界中实体来进行描述&#xff0c;类经过实例化之后的产物对象&#xff0c;则可以用来表示现实中的实体&#xff0c;但是 现实世界错综复杂&#xff0c;事物之间可能会存在一些关联&#xff0c;那在设计程…

麦歌恩MT6521-第三代汽车磁性角度传感器芯片

磁性编码芯片 -在线编程角度位置IC 描述&#xff1a; MT6521是麦歌恩微电子推出的新一代基于水平霍尔及聚磁片(IMC)技术原理的磁性角度和位置检测传感器芯片。该芯片内部包含了两对互成90放置的水平霍尔阵列及聚磁片&#xff0c;能够根据不同的型号配置来实现对XY&#xff0…

android前台服务

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、商业变现、人工智能等&#xff0c;希望大家多多支持。 未经允许不得转载 目录 一、导读二、使用2.1 添加权限2.2 新建…

MT2142 逆序(树状数组)

思路&#xff1a; 开始完全没有思路&#xff0c;还是想问题的方法不对&#xff08;应该先暴力模拟&#xff0c;然后再想可以优化的方法&#xff09;。 后来看了解析&#xff0c;用chang[]来存储每个元素删除后&#xff08;或者是该元素前面的元素删除后&#xff09;对record造成…

肿瘤演变指标预测局部晚期前列腺癌10年以上的复发| 文献速递-基于人工智能(AI base)的医学影像研究与疾病诊断

Title 题目 Tumor evolution metrics predict recurrence beyond 10 years in locally advanced prostate cancer 肿瘤演变指标预测局部晚期前列腺癌10年以上的复发 01 文献速递介绍 癌症演变为预测肿瘤学奠定了基础。测试进化指标需要在控制临床试验中进行定量测量。我们…

STM32---HAL库基础配置记录之基础配置

一&#xff1a;第一步是时钟RCC的使能配置 时钟配置界面如下&#xff0c;当我们选定好芯片型号时&#xff0c;首先需要配置的是RCC&#xff0c;如下图&#xff1a; 其中第一个HSE代表的是时钟树的高速外部时钟对应下图中的1&#xff0c;LSE代表的是下图中的2 如下图&#xff0c…

GIS场景升级:支持多种影像协议与天气效果

在GIS场景编辑领域&#xff0c;升级视效的需求日益增加。有一款名为山海鲸可视化的免费工具&#xff0c;本人亲测能够完美满足这一需求。山海鲸可视化不仅支持多种GIS影像协议&#xff08;如TMS、WMS、WMTS等&#xff09;&#xff0c;还能一键添加天气效果&#xff0c;瞬间提升…

本地电脑连接阿里云

系列文章目录 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、方法1二、使用步骤1.引入库 前言 一、方法1 本地连接远程服务器的时候提示出现身份验证错误的几种解决方法 二、使用步骤 …

算法——滑动窗口(day8)

30.串联所有单词的子串 30. 串联所有单词的子串 - 力扣&#xff08;LeetCode&#xff09; 必看&#xff01;&#xff01;&#xff01;本题是我们上次写的438.异位词的进阶版&#xff0c;可参考本篇文章&#xff1a;算法——滑动窗口&#xff08;day7&#xff09;-CSDN博客来…

MySQL数据库安装使用

我们都知道数据库又分为关系型数据库和非关系型数据库&#xff1b; 关系型数据库指采用了关系模型来组织数据的数据库&#xff0c;指的就是二维表格模型。可以先初步理解为Excel表格。非关系型数据库又被称为NoSQL&#xff0c;对NoSQL 最普遍的定义是“非关联型的”&#xff0…

Android平台RTSP|RTMP直播播放器技术接入说明

技术背景 大牛直播SDK自2015年发布RTSP、RTMP直播播放模块&#xff0c;迭代从未停止&#xff0c;SmartPlayer功能强大、性能强劲、高稳定、超低延迟、超低资源占用。无需赘述&#xff0c;全自研内核&#xff0c;行业内一致认可的跨平台RTSP、RTMP直播播放器。本文以Android平台…

免费【2024】springboot 编程语言在线学习平台的设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

数据库处理表

首先先创建库&#xff0c;然后创建需要的这三个表 用dese表名查看 然后题目要求对表进行修改 用alter table这个语法来对表进行修改 modify为修改字段 需要修改的字段的属性类型改变为的属性 最后用descStudent查看 第二题需要创建索引 创建索引createindex索引名称 cre…

C#开发的全屏图片切换效果应用 - 开源研究系列文章 - 个人小作品

这天无聊&#xff0c;想到上次开发的图片显示软件《 PhotoNet看图软件 》&#xff0c;然后想到开发一个全屏图片切换效果的应用&#xff0c;类似于屏幕保护程序&#xff0c;于是就写了此博文。这个应用比较简单&#xff0c;主要是全屏切换换图片效果的问题。 1、 项目目录&…

JS基础知识学习笔记全

JS基础知识学习笔记全 一、引入方式 1、内部脚本 &#xff08;一般定义在body下面会改善执行速度&#xff09; <body></body><!-- 内部脚本 --><script>/* 打开页面警告框显示的内容 */alert(helloJS);</script>2、外部脚本 外部专门新建一…

在Ubuntu上安装移远EC200M驱动

最近公司在做降本相关工作&#xff0c;考虑移远 EC20 4G模组成本较高&#xff0c;希望通过更低成本替换硬件&#xff0c;最后找到EC200M芯片&#xff0c;虽然EC200M速率(最大下行10M/s 最大上行5M/s)上低于EC20&#xff08;最大下行150M/s 最大上行50M/s&#xff09;,基本上可以…