泛型体系实战

news2026/3/28 6:14:14
泛型技术快速上手什么是泛型Generics定义在定义类、接口和方法时通过特定格式声明类型参数使用时再指定具体类型。作用让类、接口、方法可适配多种数据类型实现复用与编译时类型安全。特性JDK 5.0 引入的新特性支持编译时类型检查避免运行时类型转换异常。为什么要用泛型解决类型转换问题无泛型时集合默认存储Object类型取出时必须强制类型转换。若插入错误类型对象运行时会抛出ClassCastException。编译时类型校验泛型在编译期检查类型避免插入非法类型提升代码安全性。代码可读性与简洁性消除强制类型转换代码更清晰维护成本更低。组件复用封装通用组件如FIFOCache支持多种数据类型提升抽象能力。泛型的分类与命名规范分类泛型可用于类、接口、方法对应分为泛型类public class 类名 T, E, ... { ... }泛型接口interface 接口名 T, E, ... { 方法名(T t); }泛型方法修饰符 T, E, ... 返回值类型 方法名(T t) { ... }常用泛型字母命名字母含义适用场景TType任意类型通用类型占位EElement集合元素集合中元素类型KKey键Key-Value 结构中的 KeyVValue值Key-Value 结构中的 Value泛型的语法格式泛型类// 定义泛型类 public class GenericClassT { private T data; public T getData() { return data; } public void setData(T data) { this.data data; } } // 使用泛型类 GenericClassString strObj new GenericClass(); strObj.setData(xdclass); String value strObj.getData(); // 无需强制转换泛型接口// 定义泛型接口 public interface GenericInterfaceE { E getElement(); } // 实现泛型接口 public class ImplE implements GenericInterfaceE { Override public E getElement() { return null; } }泛型方法// 定义泛型方法 public T T genericMethod(T param) { return param; } // 调用泛型方法 String result genericMethod(hello); Integer num genericMethod(123);核心价值总结JDK 源码基石JDK 源码、中间件源码如 Spring、MyBatis中大量使用泛型掌握它是阅读源码的基础。提升抽象能力泛型让组件更通用、封装性更强是编写高质量框架级代码的关键。工程实践必备高并发、海量数据场景下的通用组件如缓存、工具类均依赖泛型实现。代码示例泛型实现 FIFOCacheimport java.util.LinkedList; import java.util.Queue; // 泛型版FIFOCache public class FIFOCacheK, V { private final int capacity; private final QueueK keyQueue; private final MapK, V cacheMap; public FIFOCache(int capacity) { this.capacity capacity; this.keyQueue new LinkedList(); this.cacheMap new HashMap(); } // 放入元素 public void put(K key, V value) { if (keyQueue.size() capacity) { K oldestKey keyQueue.poll(); cacheMap.remove(oldestKey); } keyQueue.offer(key); cacheMap.put(key, value); } // 获取元素 public V get(K key) { return cacheMap.get(key); } }使用FIFOCacheString, Integer cache new FIFOCache(3); cache.put(A, 1); cache.put(B, 2); System.out.println(cache.get(A)); // 输出1泛型类和案例实战 泛型类核心要点基本规则类型限制泛型类型必须是引用类型如String、Integer不能使用基本数据类型如int、double。语法格式在类名后添加内部填写类型参数多个参数用逗号分隔public class 类名 泛型类型1, 泛型类型2 { private 泛型类型1 变量名; public 泛型类型1 方法名() { ... } public 返回值 方法名(泛型类型2 t) { ... } }使用方式JDK 1.7 支持菱形语法类名具体数据类型 对象名 new 类Name();关键注意事项未指定类型时泛型类若未指定类型默认是Object类型会失去编译时类型检查。逻辑类型 vs 实际类型从逻辑上看ListString和ListInteger是不同类型但在 JVM 中实际是同一个类型受类型擦除影响。创建限制Java 允许声明泛型对象 / 数组引用但禁止直接创建泛型对象和泛型数组如new T()、new T[5]。类型擦除机制核心原理Java 泛型是伪泛型编译后会执行类型擦除所有泛型类型都会被擦除为Object类型或边界类型。因此new T()等价于new Object()无法确定具体类型编译器直接禁止此类操作。泛型数组同理直接创建会导致类型不安全所以被编译器禁止。案例实战示例java运行// 定义泛型类 public class GenericBoxT { private T data; public T getData() { return data; } public void setData(T data) { this.data data; } } // 使用泛型类JDK 1.7 菱形语法 public class Main { public static void main(String[] args) { GenericBoxString box new GenericBox(); box.setData(Hello Generics); String content box.getData(); // 无需强制类型转换 System.out.println(content); } } 总结泛型类是实现类型安全复用的核心工具通过编译时检查避免运行时类型转换异常同时要注意类型擦除带来的限制不能直接创建泛型对象 / 数组。泛型派生类和泛型接口实战 泛型类继承规则子类是泛型类父类与子类的泛型类型必须保持一致子类若有多个泛型参数必须包含父类的泛型类型。示例// 父类泛型类 public class ParentT { private T value; public T getValue() { return value; } public void setValue(T value) { this.value value; } } // 子类泛型类包含父类泛型T额外增加E、F class ChildT, E, F extends ParentT { // ... } // 子类泛型类与父类泛型完全一致 public class ChildT extends ParentT { Override public T getValue() { return super.getValue(); } }子类不是泛型类必须明确指定父类的泛型类型否则父类泛型默认擦除为Object。示例// 正确明确指定父类泛型为String class Child extends ParentString { // ... } // 不推荐未指定泛型父类泛型默认擦除为Object public class Child extends Parent { Override public Object getValue() { return super.getValue(); } } 泛型接口规则基本格式interface 接口名 泛型类型1, ... { 泛型类型 方法名(); // ... }实现类是泛型类接口与实现类的泛型类型必须一致实现类若有多个泛型参数必须包含接口的泛型类型。示例interface MyInterfaceT { T get(); } // 实现类是泛型类包含接口泛型T class MyImplT, E implements MyInterfaceT { Override public T get() { return null; } }实现类不是泛型类必须明确指定接口的泛型类型。示例class MyImpl implements MyInterfaceString { Override public String get() { return hello; } } 核心总结泛型类继承子类泛型需包含父类泛型子类非泛型则需明确父类泛型类型。泛型接口实现规则与泛型类继承完全一致核心是保持泛型类型的传递与明确性。类型擦除影响未指定泛型时默认擦除为Object会失去编译时类型检查应尽量避免。泛型方法和案例实战 泛型方法核心知识点定义与格式本质在调用方法时才指定具体类型与所在类的泛型相互独立。语法格式必须在修饰符与返回值类型之间声明T,E,...才是泛型方法修饰符 T,E,... 返回值类型 方法名(参数列表) { // 方法体 }误区提醒泛型类里返回T的普通方法如public T pop()不是泛型方法只是使用了类泛型的成员方法。独立性与作用域泛型方法的类型参数与类泛型完全独立同名也互不影响。声明的泛型类型仅在方法参数列表和方法体内有效。示例public class CustomArrayStackT { // 泛型方法E与类泛型T无关 public E E getRandomElement(ListE list) { Random random new Random(); return list.get(random.nextInt(list.size())); } }静态方法限制使用类泛型的成员方法不能定义为static因为类泛型依赖实例静态方法无实例。真正的泛型方法可以定义为static类型参数在调用时确定不依赖实例。错误示例// ❌ 错误使用类泛型T的静态方法 public static T pop() { ... }正确示例// ✅ 正确泛型方法可以是静态的 public static E E getRandomElement(ListE list) { ... }多参数与可变参数泛型方法支持多个类型参数也可搭配可变参数使用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); } } // 调用示例 print(1, 小滴课堂, 12, 32, 4, 2, 1342, 3); 总结判断标准修饰符与返回值之间有T,...才是泛型方法。独立性泛型方法的类型与类泛型无关可单独使用。静态限制只有真正的泛型方法才能定义为static使用类泛型的方法不行。泛型通配符 和案例实战 泛型通配符核心知识点定义Java 泛型通配符是解决泛型之间引用传递问题的特殊语法让泛型能更灵活地接受未知类型数据。分类与语法通配符类型语法格式含义通用通配符List?表示类型参数可以是任何类型? 可看作所有泛型类型的父类上界通配符List? extends E类型参数必须是 E 或 E 的子类包括实现了接口 E 的类下界通配符List? super E类型参数必须是 E 或 E 的超类⚠️ 注意通配符是实参不是形参不能直接用于类 / 接口的泛型声明如class CustomCollection?语法不规范正确用法是在变量 / 参数中使用。一个泛型只能指定上界或下界不能同时指定两者。关键细节上界通配符extends不仅限于继承父类的子类也包括实现了接口 E的类。示例List? extends Number可接受ListInteger、ListDouble等。下界通配符super示例List? super String可接受ListString、ListCharSequence、ListObject等。通用通配符?代表未知类型只能读取不能写入因为无法确定具体类型。常见误区修正类 / 接口声明时不能直接使用?正确写法是声明泛型形参如class CustomCollectionT通配符主要用于方法参数、变量引用场景。extends关键字不代表 “继承类”而是 “上限约束”接口实现类也满足。 总结泛型通配符让泛型的使用更灵活?接受任意类型只读安全。? extends E接受 E 及子类偏向读取场景。? super E接受 E 及超类偏向写入场景。泛型通配符上限和下限 固定上界通配符? extends E语法? extends E其中E是上边界。约束只能接受E或E的子类类型包括实现了接口E的类。示例// 只能接受 Number 或其子类Integer、Double 等 public static void printUp(NumberCollection? extends Number collection) { Number value collection.getValue(); System.out.println(value); }错误案例传入String类型会编译报错因为String不是Number的子类。注意extends不局限于类继承实现接口的类也满足约束。 固定下界通配符? super E语法? super E其中E是下边界。约束只能接受E或E的超类类型。示例// 只能接受 Integer 或其父类Number、Object 等 public static void printDown(NumberCollection? super Integer collection) { Object value collection.getValue(); System.out.println(value); }测试案例NumberCollectionLong传入会报错Long不是Integer或其父类。NumberCollectionInteger、NumberCollectionNumber可以正常传入。⚠️ 核心规则不可同时指定上下边界一个泛型只能指定extends上界或super下界不能两者同时使用。类型安全上界通配符偏向读取读取到的类型至少是E安全。下界通配符偏向写入写入E或其子类是安全的读取时类型会被擦除为Object。 总结对比通配符约束范围适用场景? extends EE 及子类读取数据保证至少是 E 类型? super EE 及超类写入数据保证能存入 E 类型泛型类型擦除 什么是泛型类型擦除背景泛型是 JDK 1.5 引入的特性为了兼容旧版本代码编译后泛型信息会被删除只保留原始类型。时机代码编译完成后、进入 JVM 运行前泛型类型信息被擦除。作用范围类泛型、接口泛型、方法泛型都会被擦除。 类型擦除的分类无限制类型擦除当泛型没有指定边界如T,K时擦除后全部变为Object类型。示例public class GenericT,K { private T age; private K name; }擦除后age和name的类型都变成Object反射打印结果为age, 类型Object name, 类型Object有限制类型擦除当泛型指定了上边界如T extends Number擦除后变为上边界类型这里是Number。示例public class GenericT extends Number, K { private T age; private K name; }擦除后age类型变为Numbername仍为Object反射打印结果为age, 类型Number name, 类型Object⚠️ 核心结论Java 泛型是伪泛型运行时不存在泛型类型只有原始类型。无边界泛型 → 擦除为Object有上边界泛型 → 擦除为上边界类型。类型擦除是泛型兼容旧代码的关键也是 “不能创建泛型对象 / 数组” 等限制的根源。 总结对比擦除类型泛型定义擦除后类型无限制TObject有限制T extends NumberNumber面试题-如何创建泛型数组和获取全部数组 泛型数组的核心痛点与解决方案背景痛点Java 中不能直接创建泛型数组如new T[size]原因是类型擦除编译后泛型信息被擦除JVM 无法确定数组的具体类型Object[]无法强制转换为T[]。直接使用Object[]存储返回时强制类型转换会抛出ClassCastException。✅ 解决方案一反射创建泛型数组推荐通过java.lang.reflect.Array.newInstance(Class? componentType, int length)反射创建指定类型的数组避开直接强制转换的陷阱。完整实现代码import java.lang.reflect.Array; public class GenericArrayT { private T[] array; // 构造器传入类类型ClassT和容量通过反射创建泛型数组 public GenericArray(ClassT cls, int capacity) { // 核心反射创建对应类型的数组并强制类型转换为T[] array (T[]) Array.newInstance(cls, capacity); } // 存入元素 public void put(int index, T item) { array[index] item; } // 获取元素 public T get(int index) { return array[index]; } // 获取原生泛型数组 public T[] getAll() { return array; } // 测试入口 public static void main(String[] args) { // 创建String类型的泛型数组容量3 GenericArrayString genericArray new GenericArray(String.class, 3); genericArray.put(0, 小滴课堂); genericArray.put(1, 架构太课); String value genericArray.get(0); System.out.println(value); // 输出小滴课堂 String[] array1 genericArray.getAll(); System.out.println(array1); // 输出正确的String数组不会报错 } }❌ 方案二直接使用 Object [] 的问题如果直接使用Object[]存储虽然能编译通过但返回数组时会抛出类型转换异常。错误示例代码public class GenericsArrayT { private Object[] array; public GenericsArray(int size) { array new Object[size]; } public void put(int index, T item) { array[index] item; } public T get(int index) { return (T) array[index]; // 单个元素可以安全强转 } // 错误无法将Object[]直接转换为T[]运行时抛出ClassCastException public T[] getAll() { return (T[]) array; } public static void main(String[] args) { GenericsArrayString ga new GenericsArray(3); ga.put(0, springcloud); String value ga.get(0); System.out.println(value); // 以下代码运行报错Cannot cast Object[] to String[] // String[] all ga.getAll(); } } 源码级解析ArrayList 的 toArray 实现JDK 集合框架如ArrayList也是通过反射 数组类型拷贝来实现泛型数组的转换核心逻辑如下核心方法链toArray()返回Object[]public Object[] toArray() { return Arrays.copyOf(elementData, size); }toArray(T[] a)泛型方法通过copyOf创建指定类型数组public T T[] toArray(T[] a) { return (T[]) Arrays.copyOf(elementData, size, a.getClass()); }Arrays.copyOf()底层最终调用Array.newInstance创建真实类型数组public static T,U T[] copyOf(U[] original, int newLength, Class? extends T[] newType) { T[] copy ((Object)newType (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; } 核心总结方案实现方式优缺点反射方案Array.newInstance(cls, capacity)能创建真实类型数组安全返回 T[]推荐使用Object [] 方案直接创建 Object[]无法直接返回类型化数组强制转换会报错JDK 集合底层复用 Array.newInstance框架级实现保证类型安全 面试必问为什么 Java 不允许直接创建泛型数组因为类型擦除编译后T会被擦除为Object或上界类型。运行时数组会被强制转换为原始类型Object[]无法保证类型安全例如ListString[]存储了ListInteger运行时无法检测。所以 Java 编译器禁止直接创建泛型数组而推荐使用反射方案。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…