Java泛型的介绍和基本使用
什么是泛型
泛型就是将类型参数化,比如定义了一个栈,你必须在定义之前声明这个栈中存放的数据的类型,是int也好是double或者其他的引用数据类型也好,定义好了之后这个栈就无法用来存放其他类型的数据。如果这时候我们想要使用这个栈来存放String(字符串类型)的数据就需要重写一遍代码,并且把数据类型改成String,可以说是代码重复性极高。
类型参数化就可以很好的解决这个问题,我们在定义栈的时候,只需要给一个形参,在使用的时候将类型作为参数传递进去就可以使用这个栈来存放不同类型的数据了。
泛型可以使用在接口,方法,和类里面,分别被称为泛型接口,泛型方法
java中的泛型标识符
下表是规范,用来增强代码的可读性,并非必须使用例如在下面的代码中使用了abc照样可以编译运行,不过可读性差,而且不规范。
下面的例子具体不用弄懂,下面会讲解,只需要知道abc和**?**在这里是通配符就好
public static <abc> void printList(List<? extends abc> list) {for (abc element : list) {System.out.println(element);} }
标记符 | 含义 |
---|---|
E | Element (在集合中使用,因为集合中存放的是元素) |
T | Type(Java 类) |
K | Key(键) |
V | Value(值) |
N | Number(数值类型) |
? | 可以表示任意类型(或者用来表示未知类型) |
有限制的通配符
上限通配符-extends
这些所谓的上和下其实是描述在一个继承关系中的上下关系,对于一个类来说,它的父类的方向就是上,而子类的方向就是下。
所以这里的"上限"通配符的“上限”指的是,一个确定的父类,或者一个确定的接口来进行限制,该通配符表示的类型都是该父类的子类(确定的接口的实现类),或者子类的子类(继承关系在这个确定的父类以下的子类或者接口的实现类)
举个栗子
public class Add<T extends Number>{private T num;Add(T num){this.num = num;}
}
怎么使用呢?
正确调用
Add<Double> a = new Add<>(1.1);//正确
Add<Integer> i = new Add<>(3);//正确
错误调用
Add<String> s = new Add<>("1.1");//错误
通过这个例子我们应该就比较好理解了,Double和Integer都是Number类的子类,所以可以代替“T extends Number”中的T,而String不在Number的继承链中所以编译会报错。
虚线下面的类,确定的接口和确定的父类就是上限通配符可以表示的类。
下限通配符-super
下限通配符也是一种对泛型类型的限制,这里的“下限”指的是继承关系中的类型的下界。
再来一个栗子
这是一个泛型方法
// 泛型方法接受 Number 及其父类的参数static void Generic(List<? super Number> list) {// 方法体}
方法调用
先看一个错误的示例
List<Integer> list = new ArrayList<>();Generic(list);
下面都是正确的示例
List<Number> list = new ArrayList<>(); Generic(list);
List<Object> list = new ArrayList<>(); Generic(list);
OK从这个例子我们应该很明白啦,List<? super Number> list的尖括号中的数据类型必须是Number及其父类。
多重限定
用来限定类型,表示指定的类型参数必须是多个指定类或指定接口的子类。
public class GenericClass<T extends ClassA & InterfaceB & InterfaceC> {/***方法和属性*/ } //表示T必须是ClassA的子类或者子类的子类并且实现了InterfaceB和InterfaceC接口,或者实现了接口的类的继承类
通过类型参数的限定,可以在泛型类或方法中对类型进行更精确的控制和约束,以提高代码的类型安全性和灵活性。。
好现在我们已经知道了泛型的常用的标识符了,现在我们来了解下泛型的三种应用
泛型类
单元泛型类
class Singl<T>{ // 这里的标识符可以随意命名,T是type的简称 private T var ; public T getVar(){ return var ; } public void setVar(T var){ this.var = var ; }
}
public class Generics{ public static void main(String args[]){ Singl<String> p = new Singl<String>() ; // 里面的var类型为String类型 p.setVar("it is love") ; // 设置字符串 System.out.println(p.getVar().length()) ; // 取得字符串的长度 }
}
多元泛型类
class Multal<K,V>{ // 这里指定了两个泛型类型 K和V private K key ; //K表示Key,V表示Valueprivate V value ; public K getKey(){ return this.key ; } public V getValue(){ return this.value ; } public void setKey(K key){ this.key = key ; } public void setValue(V value){ this.value = value ; }
}
public class GenericsClass{ public static void main(String args[]){ Multal<String,Integer> t ; // 定义两个泛型类型的对象 t = new Multal<String,Integer>() ; // 里面的key为String,value为Integer t.setKey("我爱罗") ; // 设置第一个内容 t.setValue(20) ; // 设置第二个内容 System.out.print("姓名;" + t.getKey()) ; // 取得信息 System.out.print(",年龄;" + t.getValue()) ; // 取得信息 }
}
语法:定义泛型类
class:类名<泛型标识1,泛型标识2,泛型标识3.....>{ private 泛型标识1 变量名;private 泛型标识2 变量名;
}
从泛型类派生子类
派生子类有两种情况:
- 子类(如果子类也是泛型类)与父类的类型保持一致(泛型标识保持一致),如果父类没有指明泛型类型,则按Object进行处理
- 子类不是泛型类时,父类的泛型类型必须指明 例如,如果父类没有指明,则按照Object操作
子类也是泛型类
示例一:单一类型参数的泛型类
如果子类也是泛型类,子类的泛型标识必须和父类保持一致
// 定义一个泛型类
class GenericClass<T> {private T value;public GenericClass(T value) {this.value = value;}public T getValue() {return value;}
}// 从泛型类派生子类,子类也是泛型类
class SubGenericClass<U> extends GenericClass<U> {public SubGenericClass(U value) {super(value);}public void displayValue() {System.out.println("Value: " + getValue());}
}// 使用示例
public class Main {public static void main(String[] args) {SubGenericClass<String> stringInstance = new SubGenericClass<>("Hello, Generics!");stringInstance.displayValue(); // 输出: Value: Hello, Generics!SubGenericClass<Integer> integerInstance = new SubGenericClass<>(123);integerInstance.displayValue(); // 输出: Value: 123}
}
示例 2:多个类型参数的泛型类
// 定义一个带有多个类型参数的泛型类
class Pair<K, V> {private K key;private V value;public Pair(K key, V value) {this.key = key;this.value = value;}public K getKey() {return key;}public V getValue() {return value;}
}// 从泛型类派生子类,子类也是泛型类
class ExtendedPair<K, V> extends Pair<K, V> {public ExtendedPair(K key, V value) {super(key, value);}public void display() {System.out.println("Key: " + getKey() + ", Value: " + getValue());}
}// 使用示例
public class Main {public static void main(String[] args) {ExtendedPair<String, Integer> pair = new ExtendedPair<>("Age", 30);pair.display(); // 输出: Key: Age, Value: 30ExtendedPair<Double, String> anotherPair = new ExtendedPair<>(3.14, "Pi");anotherPair.display(); // 输出: Key: 3.14, Value: Pi}
}
子类不是泛型类
定义一个泛型类
子类不是泛型类时,父类的泛型类型必须指明
// 定义一个泛型类
class GenericClass<T> {private T value;public GenericClass(T value) {this.value = value;}public T getValue() {return value;}
}
从泛型类派生的子类
// 从泛型类派生子类
class StringGenericClass extends GenericClass<String> {public StringGenericClass(String value) {super(value);}public void printValue() {System.out.println("Value: " + getValue());}
}
调用子类方法
public class Main {public static void main(String[] args) {StringGenericClass stringInstance = new StringGenericClass("Hello, Generics!");stringInstance.printValue(); // 输出: Value: Hello, Generics!}
}
带多个类型参数的泛型类
// 定义一个带有多个类型参数的泛型类
class Pair<K, V> {private K key;private V value;public Pair(K key, V value) {this.key = key;this.value = value;}public K getKey() {return key;}public V getValue() {return value;}
}// 从泛型类派生子类
class StringIntegerPair extends Pair<String, Integer> {public StringIntegerPair(String key, Integer value) {super(key, value);}public void display() {System.out.println("Key: " + getKey() + ", Value: " + getValue());}
}// 使用示例
public class Main {public static void main(String[] args) {StringIntegerPair pair = new StringIntegerPair("Age", 30);pair.display(); // 输出: Key: Age, Value: 30}
}
泛型接口
泛型接口语法
interface 接口名 <泛型标识1...>{泛型标识1 方法名(泛型标识1 变量名)}
泛型接口的使用情况(2种)
-
实现类不是泛型
- 接口类型必须明确,如果接口类型不明确,值实现类按Object处理
-
实现类是泛型
实现类为泛型类
GenericInterface 是一个泛型接口,GenericClass 是一个泛型类,继承了这个接口。GenericClass 可以接受不同类型的参数。
// 定义一个泛型接口
interface GenericInterface<T> {void display(T value);
}// 实现泛型接口的泛型类
class GenericClass<U> implements GenericInterface<U> {@Overridepublic void display(U value) {System.out.println("Value: " + value);}
}// 使用示例
public class Main {public static void main(String[] args) {GenericClass<String> stringInstance = new GenericClass<>();stringInstance.display("Hello, Generics!"); // 输出: Value: Hello, Generics!GenericClass<Integer> integerInstance = new GenericClass<>();integerInstance.display(123); // 输出: Value: 123}
}
实现类为普通类
ConcreteClass 是一个普通类,它实现了泛型接口 GenericInterface,并将类型参数指定为 String。
// 定义一个泛型接口
interface GenericInterface<T> {void display(T value);
}// 实现泛型接口的普通类
class ConcreteClass implements GenericInterface<String> {@Overridepublic void display(String value) {System.out.println("Value: " + value);}
}// 使用示例
public class Main {public static void main(String[] args) {ConcreteClass concreteInstance = new ConcreteClass();concreteInstance.display("Hello, Generics!"); // 输出: Value: Hello, Generics!}
}
泛型方法
简单的泛型方法
public class GenericMethodExample {// 定义一个泛型方法public <T> void printArray(T[] array) {for (T element : array) {System.out.println(element);}}public static void main(String[] args) {GenericMethodExample example = new GenericMethodExample();// 使用泛型方法打印字符串数组String[] stringArray = {"Hello", "Generics", "!"};example.printArray(stringArray); // 输出: Hello, Generics, !// 使用泛型方法打印整数数组Integer[] intArray = {1, 2, 3, 4, 5};example.printArray(intArray); // 输出: 1, 2, 3, 4, 5}
}
带有多个类型参数的泛型方法
public class PairUtil {// 定义一个带有多个类型参数的泛型方法public static <K, V> void printPair(K key, V value) {System.out.println("Key: " + key + ", Value: " + value);}public static void main(String[] args) {// 使用泛型方法打印键值对printPair("Name", "Alice"); // 输出: Key: Name, Value: AliceprintPair(1, 100); // 输出: Key: 1, Value: 100}
}
泛型方法,是在调用方法的时候指明泛型的具体类型。重点看下泛型的方法(图参考自:https://www.cnblogs.com/iyangyuan/archive/2013/04/09/3011274.html)
- 定义泛型方法语法格式
- 调用泛型方法语法格式
说明一下,定义泛型方法时,必须在返回值前边加一个<T>
,来声明这是一个泛型方法,持有一个泛型T
,然后才可以用泛型T作为方法的返回值。
Class<T>
的作用就是指明泛型的具体类型,而Class<T>
类型的变量c,可以用来创建泛型类的对象。
Class 的基本概念
这里着重讲一下“Class” 的含义
- Class 是一个表示某个类的对象,T 是这个类的类型参数。
- 每个类在 Java 中都有一个对应的 Class 对象,这个对象包含了该类的结构信息,如字段、方法、构造函数等。
如何获取 Class 对象
- 使用 ClassName.class
Class<String> stringClass = String.class;
- 使用 getClass() 方法
String str = "Hello";
Class<?> stringClass = str.getClass();
- 使用 Class.forName()
try {Class<?> stringClass = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {e.printStackTrace();
}
以下示例展示了如何使用 Class 来获取类的信息:
public class ClassExample {public static void main(String[] args) {// 获取 String 类的 Class 对象Class<String> stringClass = String.class;// 打印类的信息System.out.println("Class Name: " + stringClass.getName());System.out.println("Simple Name: " + stringClass.getSimpleName());System.out.println("Is Array: " + stringClass.isArray());System.out.println("Is Interface: " + stringClass.isInterface());System.out.println("Is Primitive: " + stringClass.isPrimitive());// 获取父类信息Class<?> superClass = stringClass.getSuperclass();System.out.println("Superclass: " + superClass.getName());// 获取实现的接口Class<?>[] interfaces = stringClass.getInterfaces();System.out.println("Interfaces:");for (Class<?> iface : interfaces) {System.out.println(" - " + iface.getName());}}
}
注意事项
Class 是 Java 反射机制中的一个重要组成部分,能够让程序在运行时与类进行交互,获取类的结构信息,极大地增强了 Java 的灵活性和动态性。通过理解 Class,开发者可以更有效地利用反射和泛型编程。
使用泛型来实现栈
泛型栈的定义
public class Stack<E> {private E[] arr = (E[]) new Object[10];//E[] arr = new E[10];是不允许的private int flag = 0;public void add(E x) {if(flag == arr.length ) {E[] arrnew = (E[]) new Object[arr.length * 2];for(int i = 0;i < arr.length;i++) {arrnew[i] = arr[i];}arr = arrnew;}arr[flag] = x;flag++;}public E get() {if(flag==0) {return null;}else {E x = arr[flag--];return x;}}
}
泛型栈的调用
public class Demo1 {public int x;public Demo1(int x) {this.x = x;}@Overridepublic String toString() {return "Demo1 [x=" + x + "]";}
}
public class Test {public static void main(String[] args) {Stack<Demo1> xx = new Stack<>();for(int i = 0;i < 24;i++) {xx.add(new Demo1(i));}for(int i = 0;i < 24;i++) {System.out.println(xx.get());}}
}
相关文章:

Java泛型的介绍和基本使用
什么是泛型 泛型就是将类型参数化,比如定义了一个栈,你必须在定义之前声明这个栈中存放的数据的类型,是int也好是double或者其他的引用数据类型也好,定义好了之后这个栈就无法用来存放其他类型的数据。如果这时候我们想要使用这…...

【C++】动态内存管理与模版
目录 1、关键字new: 1、用法: 2、理解: 3、与malloc的相同与不同: 1、相同: 2、不同: 2、模版初阶: 1、函数模版: 1、概念: 2、关键字:template&…...

MongoDB - 组合聚合阶段:$group、$match、$limit、$sort、$skip、$project、$count
文章目录 1. $group2. $group-> $project2.1 $group2.2 $group-> $project2.3 SpringBoot 整合 MongoDB 3. $match-> $group -> $match3.1 $match3.2 $match-> $group3.3 $match-> $group-> $match3.4 SpringBoot 整合 MongoDB 4. $match-> $group->…...

vue element-ui日期控件传参
前端:Vue element-ui <el-form-item label"过期时间" :rules"[ { required: true, message: 请选择过期时间, trigger: blur }]"><el-date-picker v-model"form.expireTime" type"date" format"yyyy-MM-dd&…...

MacOS安装SDKMan管理Java版本
文章目录 1 简介2 安装与卸载2.1 安装2.2 卸载 3 使用3.1 查看其他工具:支持 Ant, Maven 等3.2 查看Java版本3.3 安装Java,加上相关的版本3.4 设置Java版本(全局)3.5 只在当前窗口生效3.6 卸载1 默认环境无法卸载 4 jdk安装的位置5 与IDEA集成参考 1 简介…...

【网络安全的神秘世界】文件包含漏洞
🌝博客主页:泥菩萨 💖专栏:Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 一、概述 文件包含:重复使用的函数写在文件里,需要使用某个函数时直接调用此文件,而无需再…...

并发编程--volatile
1.什么是volatile volatile是 轻 量 级 的 synchronized,它在多 处 理器开 发 中保 证 了共享 变 量的 “ 可 见 性 ” 。可 见 性的意思是当一个 线 程 修改一个共享变 量 时 ,另外一个 线 程能 读 到 这 个修改的 值 。如果 volatile 变 量修 饰 符使用…...

记录unraid docker更新的域名
背景:级联 一、安装内容 unraid更新docker,之前一直失败,修改网络后可以进行安装。 二、查看域名 查看域名,发现是走github的,怪不得有一些docker无法正常更新 三、解决方法 更改代理,这里为unraid的…...

SpringCloud+Vue3多对多,多表联查
♥️作者:小宋1021 🤵♂️个人主页:小宋1021主页 ♥️坚持分析平时学习到的项目以及学习到的软件开发知识,和大家一起努力呀!!! 🎈🎈加油! 加油!…...

麒麟系统信创改造
麒麟系统信创改造 一、查看操作系统架构下载相应的依赖,压缩包1、查看Linux系统架构、CPU(1)uname -m(2)lscpu(3)cat /proc/cpuinfo(4)arch(5)getconf LONG_BIT(6)dmidecode2、根据Linux系统架构、CPU的差异进行下载相关依赖,看第二项二、以下是根据本系统的aarc…...

【Android】ListView和RecyclerView知识总结
文章目录 ListView步骤适配器AdpterArrayAdapterSimpleAdapterBaseAdpter效率问题 RecyclerView具体实现不同布局形式的设置横向滚动瀑布流网格 点击事件 ListView ListView 是 Android 中的一种视图组件,用于显示可滚动的垂直列表。每个列表项都是一个视图对象&…...

泛域名绑定到wordpress网站二级目录
要将WordPress的泛域名绑定到二级目录,你需要在你的服务器上修改Apache或Nginx配置文件。以下是两种最常见的服务器配置的示例: Apache服务器 编辑你的虚拟主机配置文件,通常位于/etc/apache2/sites-available/目录下。 <VirtualHost *…...

8、从0搭建企业门户网站——网站部署
目录 正文 1、域名解析 2、云服务器端口授权 3、Mysql数据库初始化 4、上传网站软件包 5、Tomcat配置 6、运行Tomcat 7、停止Tomcat 8、部署后发现验证码无法使用 完毕! 正文 当云服务器租用、域名购买和软件开发都完成后,我们就可以开始网站部署上线,ICP备案会长…...

uniapp中出现图片过小会与盒子偏离
结论:在image的父盒子中加上display: flex,原因不清楚 出问题的代码和图片如下: <template><view style" background-color: greenyellow; height: 10rpx;width: 10rpx;"><image :src"imgSrc.seatnull" …...

MySQL练手 --- 1934. 确认率
题目链接:1934. 确认率 思路 由题可知,两个表,一个表为Signups注册表,另一个表为Confirmations信息确认表,表的关联关系为 一对一,且user_id作为两个表的连接条件(匹配字段)&#…...

【OpenCV C++20 学习笔记】扫描图片数据
扫描图片数据 应用情景图像数据扫描的难点颜色空间缩减(color space reduction)查询表 扫描算法计算查询表统计运算时长连续内存3种扫描方法C风格的扫描方法迭代器方法坐标方法LUT方法 算法效率对比结论 应用情景 图像数据扫描的难点 在上一篇文章《基…...

LeetCode:爬楼梯(C语言)
1、问题概述:每次可以爬 1 或 2 个台阶。有多少种不同的方法可以爬到楼顶 2、示例 示例 1: 输入:n 2 输出:2 解释:有两种方法可以爬到楼顶。 1. 1 阶 1 阶 2. 2 阶 示例 2: 输入:n 3 输出&a…...

银河麒麟(arm64)环境下通过docker安装postgis3,并实现数据整体迁移
银河麒麟(arm64)环境下通过docker安装postgis3,并实现数据整体迁移 硬件配置:麒麟9006C 系统环境:银河麒麟桌面版v10 sp1 数据库:postgresql11+postgis3.0 具体的步骤参考https://blog.csdn.net/qq_34817440/article/details/103914574 -----主要操作-----------------…...

C语言 | Leetcode C语言题解之第278题第一个错误的版本
题目: 题解: int firstBadVersion(int n) {int left 1, right n;while (left < right) { // 循环直至区间左右端点相同int mid left (right - left) / 2; // 防止计算时溢出if (isBadVersion(mid)) {right mid; // 答案在区间 [left, mid] 中…...

京东科技集团将在香港发行与港元1:1挂钩的加密货币稳定币
据京东科技集团旗下公司京东币链科技(香港)官网信息,京东稳定币是一种基于公链并与港元(HKD) 1:1挂钩的稳定币,将在公共区块链上发行,其储备由高度流动且可信的资产组成,这些资产安全存放于持牌金融机构的独立账户中,通…...

Vue 实现电子签名并生成签名图片
目录 前言项目结构代码实现 安装依赖创建签名画布组件生成签名图片 总结相关阅读 1. 前言 电子签名在现代Web应用中越来越普遍,例如合同签署、确认表单等。本文将介绍如何使用Vue.js实现一个简单的电子签名功能,并将签名生成图片。 2. 项目结构 项…...

Visual Studio 2022美化
说明: VS版本:Visual Studio Community 2022 背景美化 【扩展】【管理扩展】搜索“ClaudiaIDE”,【下载】,安装完扩展要重启VS 在wallhaven下载壁纸图片作为文本编辑器区域背景图片 【工具】【选项】搜索ClaudiaIDEÿ…...

[CISCN2019 华东南赛区]Web11
进来先做信息收集,右上角显示当前ip,然后有api的调用地址和请求包的格式以及最重要的是最下面的smarty模版,一看到这个就得想到smarty模版注入 测试了一下两个api都无法访问 直接切到数据包看看能不能通过XFF来修改右上角ip 成功修改&#x…...

【图形图像-1】SDF
在图形图像处理中,SDF(Signed Distance Field,带符号的距离场)是一种表示图形轮廓和空间距离的数学结构。它通常用于计算机图形学、文本渲染、碰撞检测和物理模拟等领域。 SDF(Signed Distance Field,带符号…...

苍穹外卖01
0. 配置maven (仅一次的操作 1.项目导入idea 2. 保证nginx服务器运行 (nginx.exe要在非中文的目录下) 开启服务: start nginx 查看任务进程是否存在: tasklist /fi "imagename eq nginx.exe" 关闭ngi…...

ElasticSearch(三)—文档字段参数设置以及元字段
一、 字段参数设置 analyzer: 指定分词器。elasticsearch 是一款支持全文检索的分布式存储系统,对于 text类型的字段,首先会使用分词器进行分词,然后将分词后的词根一个一个存储在倒排索引中,后续查询主要是针对词根…...

ARM功耗管理之压力测试和PM_DEBUG实验
安全之安全(security)博客目录导读 ARM功耗管理精讲与实战汇总参见:Arm功耗管理精讲与实战 思考:睡眠唤醒实验?压力测试?Suspend-to-Idle/RAM/Disk演示? 1、实验环境准备 2、软件代码准备 3、唤醒源 4、Suspend-…...

ESP8266用AT指令实现连接MQTT
1准备工作 硬件(ESP8266)连接电脑 硬件已经烧入了MQTT透传固件 2实现连接 2-1(进入AT模式) 打开串口助手发送如下指令 AT 2-2(复位) ATRST 2-3(开启DHCP,自动获取IP&#x…...

人工智能与机器学习原理精解【5】
文章目录 最优化基础理论特征值(Eigenvalue)特征向量(Eigenvector)特征值和特征向量的重要性计算方法特征值一、特征值分解的定义二、特征值分解的算法三、特征值分解的例子 正定矩阵Hessian矩阵的特征值Hessian矩阵的含义Hessian…...

为什么用LeSS?
实现适应性 LeSS是一个产品开发的组织系统,旨在最大化一个组织的适应性。关于适应性(或者敏捷性,也就是敏捷开发的初衷)我们是指优化: 以相对低的成本改变方向的能力,主要是基于通过频繁交付产生的探索。从…...