Java核心 - 泛型详解

文章目录

  • 一、概述
    • 1、什么是泛型
    • 2、用泛型最直接的优点
    • 3、分类
  • 二、泛型详解
    • 1、泛型类
      • (1)概述
      • (2)代码实例
    • 2、泛型派生类
      • (1)概述
      • (2)代码实例
      • (3)扩展:泛型接口
    • 3、泛型方法
      • (1)概述
      • (2)代码实例
      • (3)可变参数的泛型方法
    • 4、泛型通配符
      • (1)什么是泛型通配符
      • (2)代码实例
      • (3)上边界
      • (4)下边界
    • 5、泛型擦除
      • (1)什么是泛型擦除
      • (2)代码实例
    • 6、如何创建泛型数组
      • (1)方案一:使用反射
      • (2)方案二:参考ArrayList存储Object数组

一、概述

1、什么是泛型

是在定义类、接口和方法时,可以在声明时通过一定的格式指定其参数类型。
使用时再指定具体的类型,从而使得类、接口和方法可以被多种类型的数据所实例化或调用。
这种可以在编译时进行参数类型检查的技术被称为泛型,是JDK5中引入的一个新特性。
本质是参数化类型给类型指定一个参数,在使用时再指定此参数具体的值,那这个类型就可以在使用时决定
优点:把运行时的错误,提前到编译时,这样就可以在编译时把错误提示出来,避免了运行时出现错误;使用泛型可以提高代码的复用性,因为它可以支持多种类型的数据。

2、用泛型最直接的优点

在没有泛型之前,从集合中读取到的每一个对象都必须进行类型转换。
如果插入了错误的类型对象,在运行时的转换处理就会出错。
集合容器里面如果没指定类型,默认都是Object类型,那什么到可以插入。
减少了源代码中的强制类型转换,代码更加可读。
在这里插入图片描述

3、分类

可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法

泛型字母通常类型参数都使用大写的单个字母:
T:任意类型 type
E:集合中元素的类型 element
K:key-value形式 key
V:key-value形式 value

// 泛型类
public class 类名 <泛型类型1, ...> {}// 泛型接口
interface 接口名 <泛型类型1, ...> {泛型类型 方法名();......
}// 泛型方法
修饰符 <T, E, ...> 返回值类型 方法名() {...
}

二、泛型详解

1、泛型类

(1)概述

泛型类必须是引用类型,即类类型(不能使用基本数据类型)
在类名后添加一对尖括号,并在尖括号中填写类型参数。
如果参数可以有多个,多个参数使用逗号分隔。

// 定义
public class 类名 <泛型类型, ...> {private 泛型类型 变量名;public 泛型类型 方法名() {}public 返回值 方法名(泛型类型 t) {}......
}// 使用(JDK1.7之后,结尾的具体类型可以不用写)
类名<具体数据类型> 对象名 = new 类名<>();

注意:
泛型类创建的使用没有指定类型,则默认是object类型。
泛型类型从逻辑上看是多个类型,实际都是相同类型。
Java 可以创建对应的泛型对象和泛型数组引用,但不能直接创建泛型对象和泛型数组
Java 有类型擦除,任何泛型类型在擦除之后就变成了 Object 类型,因此创建泛型对象就相当于创建了一个 Object 类型的对象,所以直接创建泛型对象和泛型数组也的行为被编译器禁止。
泛型类中使用了泛型的方法,不能定义为static!

(2)代码实例

/*** 栈* 泛型类*/
public class ArrayStack<T> {// 声明属性private Object[] arr;private int top;public ArrayStack(int capacity) {// 这样是不可以的// arr = new T[capacity];arr = new Object[capacity];top = -1;}/*** 入栈*/public void push(T value) {if (top == arr.length - 1) {throw new RuntimeException("栈满");}arr[++top] = value;}/*** 出栈*/public T pop() {if (top == -1) {throw new RuntimeException("栈空");}return (T) arr[top--];}
}
public class Test {public static void main(String[] args) {ArrayStack<String> stack = new ArrayStack<>(10);stack.push("测试");// stack.push(123); // 编译出错System.out.println(stack.pop());// 不能用基本数据类型// ArrayStack<int> stack2 = new ArrayStack<>(10);// 可以这样用ArrayStack<Integer> stack3 = new ArrayStack<>(10);// class类型是相同的System.out.println(stack.getClass());System.out.println(stack3.getClass());}
}

2、泛型派生类

(1)概述

如果泛型类的子类也是泛型类,那父类和子类的类型要一致。
如果子类泛型有多个,那需要包括父类的泛型类型。

class Child<T, E, F> extends Parent<T> {...
}

(2)代码实例

public class Parent<T> {private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}
}/*** √ 可以子类没有泛型,父类就必须指定泛型:* class Child extends Parent<String>** √ 可以子类没有泛型,父类也没有泛型,那么默认泛型就是Object类型* class Child extends Parent** √ 子类有泛型,比如要包含了父类的:* class Child<T> extends Parent<T>* class Child<T, E, F> extends Parent<T>*     * x 以下子类没有包含父类的,就会编译出错:* class Child<E, F> extends Parent<T>*/
class Child<T, E, F> extends Parent<T> {public void setValue(T value) {super.setValue(value);}
}

(3)扩展:泛型接口

interface 接口名称 <泛型类型1, ...> {泛型类型 方法名();...
}

规则和泛型类一样
如果实现类是泛型类,那接口和实现类的泛型类型要一致;如果实现类泛型有多个,那需要包括接口的泛型类型。
如果实现类不是泛型类,那接口要明确泛型类的类型。

3、泛型方法

(1)概述

调用方法的时候指定泛型的具体类型。

修饰符 <T, E, ...> 返回值类型 方法名(参数列表) {方法体
}

注意,修饰符和返回值中间有<T, E...>才是泛型方法,泛型类里面的普通返回值类型不是泛型方法。
在这里插入图片描述
泛型类的类型和泛型方法的类型是互相独立的,同名也不影响,同名的话泛型方法上的泛型优先。
声明了【泛型方法】在参数列表和方法体里面才可以用对应的泛型。
泛型方法可以定义为静态方法
在这里插入图片描述

(2)代码实例

import java.util.ArrayList;
import java.util.List;
import java.util.Random;/*** 栈* 泛型类*/
public class ArrayStack<T> {// 声明属性private Object[] arr;private int top;public ArrayStack(int capacity) {// 这样是不可以的// arr = new T[capacity];arr = new Object[capacity];top = -1;}/*** 入栈*/public void push(T value) {if (top == arr.length - 1) {throw new RuntimeException("栈满");}arr[++top] = value;}/*** 出栈*/public T pop() {if (top == -1) {throw new RuntimeException("栈空");}return (T) arr[top--];}/*** 返回一个元素** 泛型方法,泛型类的类型和泛型方法的类型是互相独立的,同名也不影响,就近优先* 声明了【泛型方法】在参数列表和方法体里面才可以用对应的泛型*/public <T> T getRandomElement(List<T> list) {Random random = new Random();return list.get(random.nextInt(list.size()));}public static void main(String[] args) {// 类泛型是IntegerArrayStack<Integer> stack = new ArrayStack<>(10);// 方法泛型是StringList<String> list = new ArrayList<>();list.add("111");list.add("222");list.add("333");String randomElement = stack.getRandomElement(list);System.out.println(randomElement);}
}

(3)可变参数的泛型方法

我们可以看出,可变参数是不限制参数的类型的。

public static void main(String[] args) {print(1, "test", "e1", "e2", 33);/*** class java.lang.Integer* class java.lang.String* e1* class java.lang.String* e2* class java.lang.String* 33* class java.lang.Integer*/
}/*** 支持多个泛型类型的,可变参数的泛型方法*/
public static <E, F, K> void print(F f, K k, E... arr) {System.out.println(f.getClass());System.out.println(k.getClass());for (E e : arr) {System.out.println(e);System.out.println(e.getClass());}
}

4、泛型通配符

(1)什么是泛型通配符

Java泛型的通配符是用于解决泛型之间引用传递间的特殊语法。

//表示类型参数可以是任何类型
public class CustomCollection<?>{)
//表示类型参数必须是A或者是A的子类
public class CustomCollection<T extends A>{)
//表示类型参数必须是A或者是A的超类型
public class CustomCollection<T supers A>{}

通用类型通配符 <?>,如List<?>
主要作用就是让泛型能够接受未知类型的数据。
可以把?看成所有泛型类型的父类,是一种真实的类型,类型通配符是实参,不是形参。

固定上边界的通配符 采用<? extends E>的形式:
使用固定上边界的通配符的泛型,只能够接受指定类及其子类类型的数据。
采用<? extends E>的形式,这里的E就是该泛型的上边界。
注意:这里虽然用的是extends关键字,却不仅限于继承了父类E的子类,也可以代指显现了接口E的类。

固定下边界的通配符,采用<? super E>的形式:
使用固定下边界的通配符的泛型,只能够接受指定类及其父类类型的数据。
采用<? super E>的形式,这里的E就是该泛型的下边界。
可以为一个泛型指定上边界或下边界,但是不能同时指定上下边界。

(2)代码实例

public class NumberCollection <T>{private T value;public NumberCollection(T value) {this.value = value;}public T getValue() {return value;}public void setValue(T value) {this.value = value;}public static void main(String[] args) {NumberCollection<Integer> numberCollection = new NumberCollection<>(1);NumberCollection<Long> numberCollection2 = new NumberCollection<>(2L);NumberCollection<String> numberCollection3 = new NumberCollection<>("3");print(numberCollection);print(numberCollection2);print(numberCollection3);}public static void print(NumberCollection<?> numberCollection) {// 使用泛型通配符,直接获取到父类Object value = numberCollection.getValue();System.out.println(value);}
}

(3)上边界

固定上边界的通配符 采用<? extends E>的形式。
使用固定上边界的通配符的泛型,只能够接受指定类及其子类类型的数据
采用<? extends E>的形式,这里的E就是该泛型的上边界。
注意:这里虽然用的是extends关键字,却不仅限于继承了父类E的子类,也可以代指显现了接口E的类。

/*** 只能接受Number及其子类的类型*/
public class NumberCollection <T extends Number>{private T value;public NumberCollection(T value) {this.value = value;}public T getValue() {return value;}public void setValue(T value) {this.value = value;}public static void main(String[] args) {NumberCollection<Integer> numberCollection = new NumberCollection<>(1);NumberCollection<Long> numberCollection2 = new NumberCollection<>(2L);// NumberCollection<String> numberCollection3 = new NumberCollection<>("3"); // 编译报错,String不属于Number的子类print(numberCollection);print(numberCollection2);// print(numberCollection3); // 编译报错,String不属于Number的子类}public static void print(NumberCollection<? extends Number> numberCollection) {// 使用泛型通配符,直接获取到父类Number value = numberCollection.getValue();System.out.println(value);}
}

在这里插入图片描述

(4)下边界

固定下边界的通配符,采用<? super E>的形式。
使用固定下边界的通配符的泛型,只能够接受指定类及其父类类型的数据
采用<? super E>的形式,这里的E就是该泛型的下边界。
可以为一个泛型指定上边界或下边界,但是不能同时指定上下边界。

public class NumberCollection <T>{private T value;public NumberCollection(T value) {this.value = value;}public T getValue() {return value;}public void setValue(T value) {this.value = value;}public static void main(String[] args) {NumberCollection<Integer> numberCollection = new NumberCollection<>(1);NumberCollection<Long> numberCollection2 = new NumberCollection<>(2L);NumberCollection<String> numberCollection3 = new NumberCollection<>("3"); // 编译报错,String不属于Number的子类print(numberCollection);// print(numberCollection2); 不可以用了// print(numberCollection3); // 编译报错,String不属于Number的子类}public static void print(NumberCollection<? super Integer> numberCollection) {// 只能获取Object类型Object value = numberCollection.getValue();System.out.println(value);}
}

5、泛型擦除

(1)什么是泛型擦除

泛型是jdk1.5后出现的,但泛型代码和常规版本代码可以兼容,主要原因是泛型信息是在代码编译阶段。
代码编译完成后进入JVM运行前,相关的泛型类型信息会被删除,这个即泛型类型擦除
作用范围:类泛型,接口泛型,方法泛型。

(2)代码实例

无限制类型擦除,擦除后都是Object类型。

有限制类型擦除,擦除后就是指定的父类。

import java.lang.reflect.Field;// 没指定类型,则擦除后是Object类型,这是最顶级的父类
public class Generic <T extends Number, K>{private T age;private K name;public static void main(String[] args) {Generic<Integer, String> generic = new Generic<>();// 反射获取字节码文件class对象Class<? extends Generic> aClass = generic.getClass();// 获取所有成员变量Field[] declaredFields = aClass.getDeclaredFields();for (Field declaredField : declaredFields) {// 获取每个属性名和类型System.out.println(declaredField.getName() + ", type is:" + declaredField.getType().getSimpleName());/*** age, type is:Number* name, type is:Object*/}}
}

6、如何创建泛型数组

背景:
在Java 中是不能直接创建泛型对象和泛型数组的。
主要原因是Java 有类型擦除,任何泛型类型在擦除之后就变成了 Object 类型或者对应的上限类型。
那定义的类中如果需要用到泛型数组,如何解决这个问题?
需求:创建一个类里面支持泛型数组和返回全部数组的方法。

(1)方案一:使用反射

import java.lang.reflect.Array;public class GenericsArray<T> {private T[] array;public GenericsArray(Class<T> clz){// 通过反射创建数组,然后强制类型转换this.array = (T[])Array.newInstance(clz, 10);}public T[] getArray() {return array;}public void setArray(T[] array) {this.array = array;}public static void main(String[] args) {GenericsArray<String> genericsArray = new GenericsArray<>(String.class);genericsArray.setArray(new String[]{"1", "2", "3"});String[] array = genericsArray.getArray();System.out.println(array);}
}

(2)方案二:参考ArrayList存储Object数组

public class GenericsArray2<T> {// 通过Object来避免泛型数组的编译问题private Object[] array;public GenericsArray2(){this.array = new Object[10];}// 无法强转,会报错public T[] getArray() {return (T[])array;}public static void main(String[] args) {GenericsArray2<String> genericsArray = new GenericsArray2<>();String[] array = genericsArray.getArray();System.out.println(array);}
}

获取数组的时候,无法进行强转,会报错。

ArrayList会最终有个toArray的方法:
在这里插入图片描述

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

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

相关文章

10元 DIY 一个柔性灯丝氛围灯

之前TikTok上特别火的线性氛围灯Augelight刚出来的时候一度卖到80多美金&#xff0c;国内1688也能到400多人民币。 随着各路国内厂商和DIY创客的跟进&#xff0c;功能变多的同时价格一路下滑&#xff0c;虽然有的质感的确感人&#xff0c;但是便宜啊。 甚至关注的up有把成本搞到…

【24年一博研讨会总结】BJ0705

1.工艺边据PCB边缘 >5mm. 2.IPC-A-600G Pitch < 1.25mm H<0.025mm,小于0.4mm,取消丝印框&#xff0c;防止锡膏不匀虚焊&#xff1b; 3.表面镀金与镍厚度 金厚度 um镍厚度 um0.0192.7830.0213.3610.0453.6640.0314.378 4.多层板&#xff0c;层间距>20mil 可以认…

Ubuntu系统复制文件到共享文件夹出错

1、问题描述 Ubuntu系统复制文件到共享文件夹时&#xff0c;出现拼接文件时出错&#xff1a;输入/输出错误。 使用cp命令&#xff1a; cp -Rf XXX YYY 也是出错&#xff1a; cp: 写入 xxx 出错: 输入/输出错误 2、查看磁盘空间 查看磁盘空间&#xff0c;显示空间还有剩余…

安乃达:看不懂的募资

不好玩啊&#xff0c;高标接连被S&#xff0c;市场激进资金找到了新股作为抱团方向。 首日大涨超100%&#xff0c;两日涨幅133%&#xff0c;今天果不其然被电风扇刮走了&#xff0c;今天我们聊聊新加入A股大本营的公司——安乃达。 首先&#xff0c;安乃达是国内直驱轮毂电机头…

零基础学python(一)

1. 匿名函数 常规函数&#xff1a; def fun(x, y):return x y 匿名函数&#xff1a; # lambda 空格后面是函数入参&#xff0c;冒号后面写函数体/函数逻辑 a lambda x,y: x y print(a(2,3)) 匿名函数/lambda函数的最大优点就是快速定义函数&#xff0c;使代码更精简。 …

【数据结构】(6.3)堆的应用——堆排序(C语言)

系列文章目录 文章目录 系列文章目录前言1. 堆排序的基础知识2. 堆排序详解2.1 堆排序整体思路2.2 思路详解2.2.1 建堆2.2.2 堆排序完整代码2.2.3 输出数据 3. 时间复杂度分析 前言 1. 堆排序的基础知识 堆排序&#xff08;Heap Sort&#xff09;就是对直接选择排序的一种改进…

04通俗理解自注意力机制(self-attention)

04浅谈自注意力机制&#xff08;self-attention&#xff09; 1. 基本概念 注意力机制 是Transformer模型的核心。它的作用是让模型能够“关注”输入数据的不同部分&#xff0c;而不是一次只处理一个词。比如&#xff0c;当模型在处理一句话时&#xff0c;它可以同时考虑句子中…

2024攻防演练:亚信安全新一代WAF,关键时刻守护先锋

实网攻防 网络安全如同一面坚固的盾牌&#xff0c;保护着我们的信息资产免受无孔不入的威胁。而其中&#xff0c;WAF就像网络安全的守门员&#xff0c;关键时刻挺身而出&#xff0c;为您的企业筑起一道坚实的防线。 攻防不对等 防守方实时应答压力山大 在攻防对抗中&#xf…

day04-matplotlib入门

matplotlib Matplotlib 提供了一个套面向绘图对象编程的 API接口 是一款用于数据可视化的 Python 软件包&#xff0c;支持跨平台运行 它能够根据 NumPyndarray 数组来绘制 2D(3D) 图像&#xff0c;它使用简单、代码清晰易懂&#xff0c;深受广大技术爱好 者喜爱。 实列&…

Django学习第五天

启动项目命令 python manage.py runserver 图像验证码生成随机字母或者数字 import random from PIL import Image, ImageDraw, ImageFont, ImageFilterdef check_code(width120, height40, char_length5, font_fileZixunHappyBold.ttf, font_size28):code []img Image.new…

Spring框架Mvc(2)

1.传递数组 代码示例 结果 2.集合参数存储并进行存储类似集合类 代码示例 postman进行测试 &#xff0c;测试结果 3.用Json来对其进行数据的传递 &#xff08;1&#xff09;Json是一个经常使用的用来表示对象的字符串 &#xff08;2&#xff09;Json字符串在字符串和对象…

图文识别0难度上手~基于飞浆对pdf简易ocr并转txt

前言 本篇pdf适用windows对视觉识别0基础的的纯小白用户。大佬请绕道~~ 注意&#xff1a; 本项目pdf的ocr对于表格、画图文字&#xff0c;水印等干扰没做任何处理&#xff0c;因此希望各位使用该功能的pdf尽量不要含有这些干扰项&#xff0c;以免影响翻译效果。 流程 1.构建…

【c语言】轻松拿捏自定义类型

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C语言 目录 前言 一、结构体 1.结构体类型的定义和使用 1.1 结构体类型声明 1.2 结构体变量的创建和初始化 1.3 结构体变量成员的访问 1.4 结构体的特殊声…

三万字带你一遍跑通uer

三万字带你一遍跑通uer 参考文档 今天给大家介绍个非常强大的项目uer&#xff0c;集成了许多可以做自然语言的东西&#xff0c;效果的话也非常好&#xff0c;很适合企业级的应用&#xff01; 1. 先将项目uer从github拉取下来&#xff08;zip或git都ok&#xff09; 2. 用pycha…

HTTP代理服务器:深度解析与应用

“随着互联网的飞速发展&#xff0c;HTTP代理服务器在网络通信中扮演着越来越重要的角色。它们作为客户端和服务器之间的中介&#xff0c;不仅优化了网络性能&#xff0c;还提供了强大的安全性和隐私保护功能。” 一、HTTP代理服务器的概念与作用 HTTP代理服务器是一种能够接…

价值499的从Emlog主题模板PandaPRO移植到wordpress的主题

Panda PRO 主题&#xff0c;一款精致wordpress博客主题&#xff0c;令人惊叹的昼夜双版设计&#xff0c;精心打磨的一处处细节&#xff0c;一切从心出发&#xff0c;从零开始&#xff0c;只为让您的站点拥有速度与优雅兼具的极致体验。 从Emlog主题模板PandaPRO移植到wordpres…

兴业小课堂|什么是法拍房助拍机构?如何挑选靠谱的助拍机构?

随着法拍房市场的不断发展和扩大 使法拍房数量的增加 其交易的复杂性和专业性需求也日益凸显 这促使了专门机构的出现来满足市场需求 法拍房助拍机构存在的原因主要有以下几点&#xff1a; 1.专业知识和经验&#xff1a; 法拍房的交易流程相对复杂&#xff0c;涉及到法律法…

【全网最全ABC三题完整版】2024年APMCM第十四届亚太地区大学生数学建模竞赛(中文赛项)完整思路解析+代码+论文

我是Tina表姐&#xff0c;毕业于中国人民大学&#xff0c;对数学建模的热爱让我在这一领域深耕多年。我的建模思路已经帮助了百余位学习者和参赛者在数学建模的道路上取得了显著的进步和成就。现在&#xff0c;我将这份宝贵的经验和知识凝练成一份全面的解题思路与代码论文集合…

第六节:如何解决@ComponentScan只能扫描当前包及子包(自学Spring boot 3.x的第一天)

大家好&#xff0c;我是网创有方&#xff0c;继上节咱们使用了Component和ComponentScan的方法实现了获取IOC容器中的Bean&#xff0c;但是存在一个问题&#xff0c;就是必须把AppConfig和要扫描的bean类放在同一个目录下&#xff0c;这样就导致了AppConfig类和bean类在同一个目…

6.8应用进程跨网络通信

《计算机网络》第7版&#xff0c;谢希仁 理解socket通信