《Java 泛型的作用与常见用法详解》
大家好呀!👋 今天我们要聊的是Java中一个超级重要但又让很多初学者头疼的概念——泛型(Generics)。带你彻底搞懂它!💪 准备好你的小本本,我们开始啦~📝
一、为什么需要泛型?🤔
想象一下,你有一个神奇的盒子📦,这个盒子可以放任何东西:苹果🍎、书本📚、甚至小猫🐱。听起来很方便对吧?但是在Java中,这样的"万能盒子"会带来大麻烦!
// 没有泛型的"万能"List
List myList = new ArrayList();
myList.add("字符串"); // 放字符串
myList.add(123); // 放数字
myList.add(new Date());// 放日期// 取出来时...
String str = (String) myList.get(1); // 运行时出错!其实是数字
看到问题了吗?😱 我们不知道盒子里到底装了什么,取出来时要强制转换,一不小心就会出错!
泛型就是来解决这个问题的!它给盒子贴上了标签🏷️:
List stringList = new ArrayList<>(); // 这个盒子只能放字符串
stringList.add("hello");
// stringList.add(123); // 编译时就报错!安全!
二、泛型基础语法速成班🎓
1. 泛型类(Generic Class)
让我们自己造一个带标签的盒子吧!
// T是类型参数,就像盒子的标签
public class MagicBox {private T content;public void put(T item) {this.content = item;}public T get() {return content;}
}// 使用示例
MagicBox stringBox = new MagicBox<>();
stringBox.put("秘密纸条");
// stringBox.put(100); // 编译错误!
String secret = stringBox.get(); // 不需要强制转换
2. 泛型方法(Generic Method)
单个方法也可以有自己的类型标签哦!
public class Tool {// 泛型方法 - 在返回类型前声明类型参数public static T getMiddle(T... items) {return items[items.length / 2];}
}// 使用示例
String middleStr = Tool.getMiddle("苹果", "香蕉", "橙子");
Integer middleNum = Tool.getMiddle(1, 2, 3); // 可以省略类型参数
3. 泛型接口(Generic Interface)
接口也可以带标签!
public interface Storage {void store(T item);T retrieve();
}// 实现类需要指定具体类型
public class FileStorage implements Storage {@Overridepublic void store(String item) { /*...*/ }@Overridepublic String retrieve() { /*...*/ }
}
三、泛型高级用法🚀
1. 类型通配符(Wildcards)
有时候我们不知道盒子里具体是什么,但知道大概范围:
// 未知类型的盒子
public void peekBox(MagicBox box) {System.out.println("盒子里有东西,但我不知道是啥");
}// 必须是Number或其子类的盒子
public void sumNumbers(MagicBox box) {Number num = box.get();System.out.println(num.doubleValue() + 10);
}// 必须是Integer或其父类的盒子
public void setInteger(MagicBox box) {box.put(100);
}
记忆口诀📝:
- ``:随便啥都行
- ``:T或T的子类(上界)
- ``:T或T的父类(下界)
2. 泛型擦除(Type Erasure)
Java泛型是编译期的魔法🔮,运行时类型信息会被擦除:
List stringList = new ArrayList<>();
List intList = new ArrayList<>();// 运行时都是ArrayList,类型参数被擦除了
System.out.println(stringList.getClass() == intList.getClass()); // true
这也是为什么我们不能这样写:
// 编译错误!
public class MyClass {private T instance = new T(); // 不知道T有没有无参构造private T[] array = new T[10]; // 数组必须知道具体类型
}
3. 泛型与数组的恩怨情仇💔
泛型数组是个特殊的存在:
// 这样不行!
List[] arrayOfLists = new List[10]; // 编译错误// 但这样可以(会有警告)
List[] arrayOfLists = (List[]) new List[10];
为什么这么设计?因为数组在运行时需要知道具体类型来保证类型安全,而泛型会被擦除,两者机制冲突了。
四、实际开发中的泛型应用场景🏗️
1. 集合框架(Collections)
这是泛型最常用的地方:
// 传统方式(不建议)
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); // 需要强制转换// 泛型方式(推荐)
List list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // 自动转换
2. 函数式接口(Functional Interfaces)
配合Lambda表达式使用:
// 定义泛型函数式接口
@FunctionalInterface
interface Converter {R convert(T from);
}// 使用示例
Converter converter = Integer::valueOf;
Integer num = converter.convert("123");
3. 构建工具类
比如一个通用的Pair类:
public class Pair {private final T first;private final U second;public Pair(T first, U second) {this.first = first;this.second = second;}// getters...
}// 使用示例
Pair nameAndAge = new Pair<>("张三", 25);
4. 反射中的泛型
获取泛型类型信息:
public class GenericClass {public List getStringList() {return new ArrayList<>();}
}// 获取方法的泛型返回类型
Method method = GenericClass.class.getMethod("getStringList");
Type returnType = method.getGenericReturnType();if (returnType instanceof ParameterizedType) {ParameterizedType type = (ParameterizedType) returnType;Type[] typeArguments = type.getActualTypeArguments();for (Type typeArg : typeArguments) {System.out.println(typeArg); // 输出: class java.lang.String}
}
五、常见问题解答❓
Q1: 为什么不能直接创建泛型数组?
A: 因为数组需要在运行时知道确切类型来保证类型安全,而泛型在运行时会被擦除,导致可能的类型不安全。
Q2: List 和 List 有什么区别?
A:
List是明确存储Object类型元素的列表List是存储未知类型元素的列表,更灵活但限制更多
List objectList = new ArrayList<>();
objectList.add("字符串"); // 可以
objectList.add(123); // 可以List wildcardList = new ArrayList();
// wildcardList.add("hello"); // 编译错误!不知道具体类型
Q3: 泛型方法中的``和返回类型前的T有什么关系?
A: 方法声明中的``是类型参数声明,返回类型前的T是使用这个类型参数。它们必须匹配:
// 正确:声明T并使用T
public static T method1(T param) { ... }// 错误:声明T却使用U
public static U method2(T param) { ... } // 编译错误!
六、最佳实践与陷阱规避🚧
1. 命名约定
类型参数通常用单个大写字母:
- E - Element (集合中使用)
- K - Key (键)
- V - Value (值)
- T - Type (类型)
- S,U,V - 第二、第三、第四类型
2. 避免原生类型
// 不好!
List list = new ArrayList(); // 原生类型// 好!
List list = new ArrayList<>(); // 参数化类型
3. 谨慎使用通配符
// 过度使用通配符会让代码难以理解
public void process(List> list) { ... }// 适当拆分更清晰
public > void process(List list) { ... }
4. 类型安全的异构容器
有时候我们需要一个容器能存储多种不同类型:
public class TypeSafeContainer {private Map, Object> map = new HashMap<>();public void put(Class type, T instance) {map.put(Objects.requireNonNull(type), type.cast(instance));}public T get(Class type) {return type.cast(map.get(type));}
}// 使用示例
TypeSafeContainer container = new TypeSafeContainer();
container.put(String.class, "字符串");
container.put(Integer.class, 123);String s = container.get(String.class);
Integer i = container.get(Integer.class);
七、Java 8/9/10/11中的泛型改进🌈
1. 钻石操作符改进 (Java 7)
// Java 7之前
List list = new ArrayList();// Java 7+ 可以省略右边的类型参数
List list = new ArrayList<>();
2. 局部变量类型推断 (Java 10)
// Java 10+
var list = new ArrayList(); // 自动推断为ArrayList
3. 匿名类的钻石操作符 (Java 9)
// Java 9+ 匿名类也可以使用钻石操作符
List list = new ArrayList<>() {// 匿名类实现
};
八、终极挑战:你能回答这些问题吗?🧠
-
下面代码有什么问题?
public class Box {private T[] items = new T[10]; // 哪里错了? } -
下面两个方法签名有什么区别?
void printList(List list) void printList(List list) -
如何编写一个方法,接受任何List,但只能添加Number及其子类?
(答案在文末👇)
九、总结与思维导图🎯
让我们用一张图总结泛型的核心要点:
Java泛型
├── 为什么需要?
│ ├── 类型安全
│ └── 消除强制转换
├── 基础语法
│ ├── 泛型类 class Box
│ ├── 泛型方法 T method(T t)
│ └── 泛型接口 interface Store
├── 高级特性
│ ├── 通配符 ?
│ │ ├── 上界 ? extends T
│ │ └── 下界 ? super T
│ └── 类型擦除
└── 应用场景├── 集合框架├── 工具类└── 函数式编程
十、实战练习💻
练习1:实现通用缓存类
// 实现一个通用缓存类,可以存储任意类型,但每种类型只能存储一个实例
public class TypeCache {// 你的代码...
}// 使用示例
TypeCache cache = new TypeCache();
cache.put(String.class, "hello");
String value = cache.get(String.class);
练习2:编写泛型工具方法
// 编写一个方法,将任意类型数组转换为ArrayList
public static ArrayList arrayToList(T[] array) {// 你的代码...
}
终极挑战答案:
- 不能直接创建泛型数组,应该使用Object数组然后强制转换:
private T[] items = (T[]) new Object[10]; - 第一个只能接受List,第二个可以接受任何List
void addNumbers(List list) {list.add(Integer.valueOf(1));list.add(Double.valueOf(2.0));
}
好啦!这篇超详细的Java泛型指南就到这里啦!👏 希望你现在对泛型有了全面的理解。如果有任何问题,欢迎在评论区留言讨论哦~💬 记得点赞收藏,下次见!😘
推荐阅读文章
-
由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)
-
如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系
-
HTTP、HTTPS、Cookie 和 Session 之间的关系
-
什么是 Cookie?简单介绍与使用方法
-
什么是 Session?如何应用?
-
使用 Spring 框架构建 MVC 应用程序:初学者教程
-
有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
-
如何理解应用 Java 多线程与并发编程?
-
把握Java泛型的艺术:协变、逆变与不可变性一网打尽
-
Java Spring 中常用的 @PostConstruct 注解使用总结
-
如何理解线程安全这个概念?
-
理解 Java 桥接方法
-
Spring 整合嵌入式 Tomcat 容器
-
Tomcat 如何加载 SpringMVC 组件
-
“在什么情况下类需要实现 Serializable,什么情况下又不需要(一)?”
-
“避免序列化灾难:掌握实现 Serializable 的真相!(二)”
-
如何自定义一个自己的 Spring Boot Starter 组件(从入门到实践)
-
解密 Redis:如何通过 IO 多路复用征服高并发挑战!
-
线程 vs 虚拟线程:深入理解及区别
-
深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
-
10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
-
“打破重复代码的魔咒:使用 Function 接口在 Java 8 中实现优雅重构!”
-
Java 中消除 If-else 技巧总结
-
线程池的核心参数配置(仅供参考)
-
【人工智能】聊聊Transformer,深度学习的一股清流(13)
-
Java 枚举的几个常用技巧,你可以试着用用
-
由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)
-
如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系
-
HTTP、HTTPS、Cookie 和 Session 之间的关系
-
使用 Spring 框架构建 MVC 应用程序:初学者教程
-
有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
-
Java Spring 中常用的 @PostConstruct 注解使用总结
-
线程 vs 虚拟线程:深入理解及区别
-
深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
-
10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
-
探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)
-
为什么用了 @Builder 反而报错?深入理解 Lombok 的“暗坑”与解决方案(二)
相关文章:
《Java 泛型的作用与常见用法详解》
大家好呀!👋 今天我们要聊的是Java中一个超级重要但又让很多初学者头疼的概念——泛型(Generics)。带你彻底搞懂它!💪 准备好你的小本本,我们开始啦~📝 一、为什么需要泛型?&#x…...
【JavaWeb】详细讲解 HTTP 协议
文章目录 一、HTTP简介1.1 概念1.2 特点 二、协议2.1 HTTP-请求协议(1)GET方式(2)POST方式(3)GET和POST的区别: 2.2 HTTP-响应协议(1)格式(2)响应…...
【android bluetooth 框架分析 02】【Module详解 4】【Btaa 模块介绍】
1. 背景 我们在上一篇文章中介绍 HciHal 模块时,有如下代码 // system/gd/hal/hci_hal_android_hidl.ccvoid ListDependencies(ModuleList* list) const {list->add<SnoopLogger>();if (common::init_flags::btaa_hci_is_enabled()) {list->add<ac…...
“星睿O6” AI PC开发套件评测 - Windows on Arm 安装指南和性能测评
引言 Radxa联合此芯科技和安谋科技推出全新的"星睿O6"迷你 ITX 主板。该系统搭载了 CIX P1(CD8180)12 核 Armv9 处理器,拥有高达30T算力的NPU和高性能的GPU,最高配备64GB LPDDR内存,并提供了如 5GbE、HDMI …...
Python 调用 YOLOv11 ONNX
Python 调用 YOLO ONNX 1 下载ONNX文件2 Python代码 1 下载ONNX文件 ONNX下载地址 2 Python代码 import cv2 from ultralytics import YOLOdef check(yolo:str, path:str):# 加载 YOLOv11model YOLO(yolo)# 读取图片img cv2.imread(path)# 推理(可以传文件路径…...
数据通信学习笔记之OSPF路由汇总
区域间路由汇总 路由汇总又被称为路由聚合,即是将一组前缀相同的路由汇聚成一条路由,从而达到减小路由表规模以及优化设备资源利用率的目的,我们把汇聚之前的这组路由称为精细路由或明细路由,把汇聚之后的这条路由称为汇总路由或…...
ASP.NET Core Web API 配置系统集成
文章目录 前言一、配置源与默认设置二、使用步骤1)创建项目并添加配置2)配置文件3)强类型配置类4)配置Program.cs5)控制器中使用配置6)配置优先级测试7)动态重载配置测试8)运行结果示…...
如何判断单片机性能极限?
目录 1、CPU 负载 2、内存使用情况 3、实时性能 4、外设带宽 5、功耗与温度 在嵌入式系统设计中,当系统变得复杂、功能增加时,单片机可能会逐渐逼近其性能极限。及时识别这些极限点对于保证产品质量、稳定性和用户体验至关重要。 当你的嵌入式系统…...
AI在多Agent协同领域的核心概念、技术方法、应用场景及挑战 的详细解析
以下是 AI在多Agent协同领域的核心概念、技术方法、应用场景及挑战 的详细解析: 1. 多Agent协同的定义与核心目标 多Agent系统(MAS, Multi-Agent System): 由多个独立或协作的智能体(Agent)组成ÿ…...
1.凸包、极点、极边基础概念
目录 1.凸包 2.调色问题 3.极性(Extrem) 4.凸组合(Convex Combination) 5.问题转化(Strategy)编辑 6.In-Triangle test 7.To-Left-test 8.极边(Extream Edges) 1.凸包 凸包就是上面蓝色皮筋围出来的范围 这些钉子可以转换到坐标轴中࿰…...
OSCP - Proving Grounds - DriftingBlues6
主要知识点 路径爆破dirtycow内核漏洞提权 具体步骤 总体来讲,这台靶机还是比较直接的,没有那么多的陷阱,非常适合用来学习 依旧是nmap开始,只开放了80端口 Nmap scan report for 192.168.192.219 Host is up (0.42s latency). Not shown: 65534 cl…...
深度理解指针之例题
文章目录 前言题目分析与讲解涉及知识点 前言 对指针有一定了解后,讲一下一道初学者的易错题 题目分析与讲解 先定义一个数组跟一个指针变量 然后把数组名赋值给指针变量————也就是把首地址传到pulPtr中 重点是分析这一句: *(pulPtr…...
java 设计模式之策略模式
简介 策略模式:策略模式可以定制目标对象的行为,它尅通过传入不同的策略实现,来配置目标对象的行为。使用策略模式,就是为了定制目标对象在某个关键点的行为。 策略模式中的角色: 上下文类:持有一个策略…...
LeetCode算法题(Go语言实现)_51
题目 给你两个下标从 0 开始的整数数组 nums1 和 nums2 ,两者长度都是 n ,再给你一个正整数 k 。你必须从 nums1 中选一个长度为 k 的 子序列 对应的下标。 对于选择的下标 i0 ,i1 ,…, ik - 1 ,你的 分数 …...
条件变量condition_variable
1.条件变量用来控制线程同步和协调。 2.调用wait方法,如果条件满足就不挂起,如果条件不满足就挂起线程并释放锁。 3.等到通知后,挂起线程先 竞争锁,然后 判断条件,如果满足条件就往下走,如果不满足就再次挂起并释放锁…...
Solon AI MCP Server 入门:Helloworld (支持 java8 到 java24。国产解决方案)
目前网上能看到的 MCP Server 基本上都是基于 Python 或者 nodejs ,虽然也有 Java 版本的 MCP SDK,但是鲜有基于 Java 开发的。 作为Java 开发中的国产顶级框架 Solon 已经基于 MCP SDK 在进行 Solon AI MCP 框架开发了,本文将使用 Solon AI …...
Maven工具学习使用(十一)——部署项目到仓库
1、使用Maven默认方式 Maven 部署项目时默认使用的上传文件方式是通过 HTTP/HTTPS 协议。要在 Maven 项目中配置部署,您需要在项目的 pom.xml 文件中添加 部分。这个部分定义了如何部署项目的构件(如 JAR 文件)到仓库。。这个部分定义了如何…...
公司内部自建知识共享的方式分类、详细步骤及表格总结,分为开源(对外公开)和闭源(仅限内部),以及公共(全员可访问)和内部(特定团队/项目组)四个维度
以下是公司内部自建知识共享的方式分类、详细步骤及表格总结,分为开源(对外公开)和闭源(仅限内部),以及公共(全员可访问)和内部(特定团队/项目组)四个维度&am…...
Oracle 19c部署之初始化实例(三)
上一篇文章中,我们已经完成了数据库软件安装,接下来我们需要进行实例初始化工作。 一、初始化实例的两种方式 1.1 图形化初始化实例 描述:图形化初始化实例是通过Oracle的Database Configuration Assistant (DBCA)工具完成的。用户通过一系…...
医疗设备预测性维护合规架构:从法规遵循到技术实现的深度解析
在医疗行业数字化转型加速推进的当下,医疗设备预测性维护已成为提升设备可用性、保障医疗安全的核心技术。然而,该技术的有效落地必须建立在严格的合规框架之上。医疗设备直接关乎患者生命健康,其维护过程涉及医疗法规、数据安全、质量管控等…...
Openfeign的最佳实践
文章目录 问题引入一、继承的方式1. 建立独立的Moudle服务2. 服务调用方继承jar包中的接口3. 直接注入继承后的接口进行使用 二、抽取的方式1. 建立独立的Moudle服务2.服务调用方依赖注入 问题引入 openfeign接口的实现和服务提供方的controller非常相似,例如&…...
Python中如何加密/解密敏感信息(如用户密码、token)
敏感信息,如用户密码、API密钥、访问令牌(token)、信用卡号以及其他个人身份信息(PII),构成了现代应用程序和系统中最为关键的部分。这些信息一旦被未经授权的第三方获取,可能引发灾难性的后果,从个人隐私泄露到企业经济损失,甚至是大规模的社会安全问题。保护这些敏感…...
【Java面试系列】Spring Cloud微服务架构中的分布式事务解决方案与Seata框架实现原理详解 - 3-5年Java开发必备知识
【Java面试系列】Spring Cloud微服务架构中的分布式事务解决方案与Seata框架实现原理详解 - 3-5年Java开发必备知识 引言 在微服务架构中,分布式事务是一个不可避免的挑战。随着业务复杂度的提升,如何保证跨服务的数据一致性成为了面试中的高频问题。本…...
从万维网到人工智能基石:大数据技术三十年演进史(1991-2025)
一、万维网的创世纪(1991) 1.1 信息共享的革命性突破 1991年8月6日,蒂姆伯纳斯-李在欧洲核子研究中心(CERN)发布首个万维网(World Wide Web)网站,构建了信息互联的三项核心技术&…...
Buildroot编译过程中下载源码失败
RK3588编译一下recovery,需要把buildroot源码编译一遍。遇到好几个文件都下载失败,如下所示 pm-utils 1.4.1这个包下载失败,下载地址http://pm-utils.freedesktop.org/releases 解决办法,换个网络用windows浏览器下载后ÿ…...
【Rust基础】crossbeam带来的阻塞问题
背景 最近正在做AI知识库的相关内容,web框架使用Rocket,需要使用SSE处理模型的流式输出,而Rocket的SSE功能比较单一,没有进行全局状态管理,因此需要手动处理SSE连接,而对于web环境下,必然会涉及…...
OpenCV 图形API(43)颜色空间转换-----将 BGR 图像转换为 LUV 色彩空间函数BGR2LUV()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 将图像从BGR色彩空间转换为LUV色彩空间。 该函数将输入图像从BGR色彩空间转换为LUV。B、G和R通道值的传统范围是0到255。 输出图像必须是8位无符…...
自问自答模式(Operation是什么)
自问自答 问:Operation 注解来自哪里? 答:Operation 是 OpenAPI(Swagger)规范中,来自 io.swagger.v3.oas.annotations 包的一个注解,用于给 REST 接口增加文档元数据。 问:summary …...
996引擎-实战笔记:Lua 的 NPC 面板获取 Input 内容
996引擎-实战笔记:Lua 的 NPC 面板获取 Input 内容 获取 Input 内容测试NPC参考资料获取 Input 内容 测试NPC -- NPC入口函数 function main(player)local msg = [[<Img|id=9527|x=0|y=0|width=300|height=150|img=public/bg_npc_01.png|bg=1|move=1|reset=1|show=0|layer…...
少数服从多数悖论、黑白颠倒与众人孤立现象之如何应对(一)
观己之前,也可先观众生 如果当时没有袖手旁观,或许唇不亡齿也不会寒 ■如何轻松/更好应对个别被众人孤立(他人、辨别、自己) ●他人被孤立 不参与 有余力,助弱者 被孤立者本身有问题 •不参与:不会辨…...
