目录

一、概述

二、为什么使用泛型

三、常见泛型类型参数的含义与用途

示例一:使用 T 定义泛型类

示例二:使用 E 表示集合元素

示例三:使用 K 和 V 表示键值对

示例四:使用 ? 通配符处理未知类型

四、通配符 ? 的扩展用法

无界通配符:?

上限通配符:? extends T

下限通配符:? super T

五、泛型类别

泛型类

泛型接口

泛型方法

六、泛型的类型表示:Type 接口的实现

Class:非泛型类型

ParameterizedType:参数化类型

TypeVariable:类型变量

WildcardType:通配符类型

七、其他泛型写法

多边界类型参数

通配符嵌套

递归类型约束

泛型与函数式接口结合

泛型数组的创建技巧

八、注意事项

总结


Java 泛型(Generics)是 Java 5 引入的重要特性之一,它允许在类、接口和方法中使用类型参数,从而实现类型安全的复用代码。泛型不仅提高了代码的可重用性,还增强了编译时的类型检查,避免了运行时的类型转换错误。

元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。如:Collection<E>,List<E>,ArrayList<E>这个<E>就是类型参数,即泛型

在使用泛型的过程中,我们经常会看到 T、E、K、V、? 等符号,它们分别代表不同的类型参数。本文将详细解析这些符号的含义、用途以及最佳实践,帮助你不再“傻傻分不清”。

一、概述

泛型的核心思想是 “参数化类型”,即把类型作为参数传递给类或方法。例如:

List<String> list = new ArrayList<>();

这里的 String 就是类型参数,表示这个列表只能存放字符串类型的元素。

二、为什么使用泛型

泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

  • 保证了类型的安全性:在没有泛型之前,从集合中读取到的每一个对象都必须进行类型转换,如果不小心插入了错误的类型对象,在运行时的转换处理就会出错

  • 消除强制转换:泛型的一个附带好处是,消除源代码中的许多强制类型转换,这使得代码更加可读,并且减少了出错机会

  • 避免了不必要的装箱、拆箱操作,提高程序的性能

  • 提高了代码的重用性

三、常见泛型类型参数的含义与用途

Java 泛型中并没有强制规定必须使用哪些字母作为类型参数,但为了代码的可读性和一致性,社区中形成了一些约定俗成的命名习惯。

类型参数含义常见使用场景

T

Type(类型)

表示任意具体类型,常用于类、接口、方法级别的泛型定义

E

Element(元素)

多用于集合类中,表示集合中的元素类型,如 List<E>

K

Key(键)

通常用于 Map 中的键类型,如 Map<K, V>

V

Value(值)

通常用于 Map 中的值类型,如 Map<K, V>

N

Number(数字)

表示数值类型,如 Number的子类

?

Unknown(未知)

表示不确定的类型,用于通配符(Wildcard)

S, U, V

第二、第三、第四个类型参数

当一个泛型需要多个类型参数时使用

示例一:使用 T 定义泛型类

public class Box<T> {private T item;public void setItem(T item) {this.item = item;}public T getItem() {return item;}
}

调用方式:

Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");Box<Integer> integerBox = new Box<>();
integerBox.setItem(123);

示例二:使用 E 表示集合元素

public interface List<E> extends Collection<E> {// ...
}

示例三:使用 K 和 V 表示键值对

public interface Map<K, V> {V put(K key, V value);V get(Object key);
}

示例四:使用 ? 通配符处理未知类型

public static void printList(List<?> list) {for (Object obj : list) {System.out.println(obj);}
}

此方法可以接受任何类型的 List,比如 List、List等。

四、通配符 ? 的扩展用法

通配符 ? 可以配合上下限使用,实现更灵活的类型约束。

无界通配符:?

适用于不知道或不关心具体类型的情况。

List<?> anyList = new ArrayList<String>();

上限通配符:? extends T

表示某种类型及其子类,适合“只读”操作。

public static void addNumbers(List<? extends Number> list) {for (Number number : list) {System.out.println(number.doubleValue());}
}

注意:不能向这种列表中添加元素(除了 null),因为编译器无法确定具体的类型。

下限通配符:? super T

表示某种类型及其父类,适合“写入”操作。

public static void addIntegers(List<? super Integer> list) {list.add(100); // 正确:Integer 是下限的子类
}

此时你可以往列表中添加 Integer 类型的数据,但不能保证读取的是什么类型。

五、泛型类别

泛型根据定义位置和作用范围,可分为泛型类、泛型接口和泛型方法三类。它们的核心都是 “参数化类型”,但适用场景和语法略有差异。

泛型类

泛型类是指在类定义时声明类型参数,使得类中的字段、方法参数或返回值可以使用这些类型参数。类型参数的作用域贯穿整个类。

public class DataWrapper<T> {private T data;public DataWrapper(T data) {this.data = data;}public T getData() {return data;}public void setData(T data) {this.data = data;}
}

         泛型类继承,若父类指定类型,则子类不用指定,默认和父类一样。若父类没有指定泛型类型,则父类、子类都需要默认泛型(Object)

泛型接口

泛型接口与泛型类类似,在接口定义时声明类型参数,实现类需指定具体类型或继续保留泛型。

// 定义泛型接口,E表示产出的元素类型
public interface Producer<E> {E produce();
}// 实现接口时指定具体类型(如String)
public class StringProducer implements Producer<String> {@Overridepublic String produce() {return "Hello";}
}// 实现接口时继续保留泛型(泛型实现类)
public class GenericProducer<T> implements Producer<T> {private T data;public GenericProducer(T data) {this.data = data;}@Overridepublic T produce() {return data;}
}

泛型方法

泛型方法是指在方法声明时单独声明类型参数的方法,其类型参数的作用域仅限于当前方法。它可以是静态方法或实例方法,且无需依赖所在类是否为泛型类。

public class GenericMethodDemo {// 泛型实例方法:将输入对象转换为目标类型(简化示例)public <T> T convert(Object obj, Class<T> clazz) throws Exception {if (clazz.isInstance(obj)) {return clazz.cast(obj);}// 实际场景可能通过反射、JSON等方式转换return clazz.getConstructor(String.class).newInstance(obj.toString());}// 静态泛型方法:创建指定类型的列表并添加元素public static <E> List<E> createList(E... elements) {List<E> list = new ArrayList<>();for (E e : elements) {list.add(e);}return list;}
}// 使用泛型方法
public static void main(String[] args) throws Exception {GenericMethodDemo demo = new GenericMethodDemo();// 实例泛型方法:将String转换为IntegerInteger num = demo.convert("123", Integer.class);// 静态泛型方法:创建String列表List<String> strList = GenericMethodDemo.createList("a", "b", "c");
}

关键特性

  • 泛型方法的类型参数声明(如)必须在返回值前,这是区分泛型方法与普通方法的核心标志;

  • 静态方法无法使用所在类的泛型参数(因静态成员属于类,与实例无关),若需泛型需定义为静态泛型方法;

六、泛型的类型表示:Type 接口的实现

在 Java 中,泛型的类型信息在编译后会被部分保留(类型擦除不会完全抹去所有信息),这些信息可通过反射 API 获取。java.lang.reflect.Type接口是所有泛型类型的父接口,其下有 5 种核心实现(或子接口),用于表示不同场景的泛型类型。

Class<?>:非泛型类型

Class是Type的直接实现,用于表示非泛型类型(如String、Integer)或泛型类型的原始类型(如List,未指定类型参数的泛型类)。

// String是普通类,类型为Class
Class<String> stringClass = String.class;
// List是泛型类的原始类型,类型仍为Class
Class<List> listClass = List.class;

ParameterizedType:参数化类型

表示指定了具体类型参数的泛型类型,如List、Map<Integer, String>。其核心方法包括:

  • getRawType():获取原始类型(如List的原始类型是List.class);

  • getActualTypeArguments():获取实际类型参数(如List的实际参数是String.class)。

public class ParameterizedTypeDemo {private List<String> stringList;private Map<Integer, List<String>> complexMap;public static void main(String[] args) throws NoSuchFieldException {// 获取stringList字段的类型Field field1 = ParameterizedTypeDemo.class.getDeclaredField("stringList");ParameterizedType pt1 = (ParameterizedType) field1.getGenericType();System.out.println(pt1.getRawType()); // interface java.util.ListSystem.out.println(pt1.getActualTypeArguments()[0]); // class java.lang.String// 获取complexMap字段的类型Field field2 = ParameterizedTypeDemo.class.getDeclaredField("complexMap");ParameterizedType pt2 = (ParameterizedType) field2.getGenericType();System.out.println(pt2.getRawType()); // interface java.util.MapSystem.out.println(pt2.getActualTypeArguments()[0]); // class java.lang.IntegerSystem.out.println(pt2.getActualTypeArguments()[1]); // java.util.List<java.lang.String>(也是ParameterizedType)}
}

TypeVariable:类型变量

表示泛型中的类型参数(如类或方法声明的T、E)。其核心方法包括:

  • getName():获取类型参数名称(如T);

  • getBounds():获取类型参数的上限(如class Box中,T的上限是Number.class)。

public class TypeVariableDemo<T extends Number & Serializable, E> {public static void main(String[] args) {// 获取类的泛型类型参数TypeVariable<Class<TypeVariableDemo>>[] typeVariables = TypeVariableDemo.class.getTypeParameters();// 第一个类型参数TTypeVariable<Class<TypeVariableDemo>> t = typeVariables[0];System.out.println(t.getName()); // TSystem.out.println(Arrays.toString(t.getBounds())); // [class java.lang.Number, interface java.io.Serializable]// 第二个类型参数E(无显式上限,默认上限为Object)TypeVariable<Class<TypeVariableDemo>> e = typeVariables[1];System.out.println(e.getName()); // ESystem.out.println(Arrays.toString(e.getBounds())); // [class java.lang.Object]}
}

WildcardType:通配符类型

表示泛型通配符(如?、? extends Number、? super Integer)。其核心方法包括:

  • getUpperBounds():获取上限(如? extends Number的上限是Number.class);

  • getLowerBounds():获取下限(如? super Integer的下限是Integer.class)。

public class WildcardTypeDemo {private List<? extends Number> upperList;private List<? super Integer> lowerList;public static void main(String[] args) throws NoSuchFieldException {// 获取上限通配符Field field1 = WildcardTypeDemo.class.getDeclaredField("upperList");ParameterizedType pt1 = (ParameterizedType) field1.getGenericType();WildcardType wt1 = (WildcardType) pt1.getActualTypeArguments()[0];System.out.println(Arrays.toString(wt1.getUpperBounds())); // [class java.lang.Number]System.out.println(Arrays.toString(wt1.getLowerBounds())); // [](无下限)// 获取下限通配符Field field2 = WildcardTypeDemo.class.getDeclaredField("lowerList");ParameterizedType pt2 = (ParameterizedType) field2.getGenericType();WildcardType wt2 = (WildcardType) pt2.getActualTypeArguments()[0];System.out.println(Arrays.toString(wt2.getUpperBounds())); // [class java.lang.Object](默认上限)System.out.println(Arrays.toString(wt2.getLowerBounds())); // [class java.lang.Integer]}
}

为什么需要了解 Type 接口?

在日常开发中,直接使用这些类型的场景较少,但在框架开发(如 Spring、Jackson)中,反射处理泛型是常见需求。例如:

  • Jackson 解析List时,需通过ParameterizedType获取User类型;

  • Spring 的ResolvableType工具类封装了对这些类型的处理,用于依赖注入时的泛型匹配。

七、其他泛型写法

Java 泛型的高级写法主要体现在多边界约束、通配符嵌套、泛型方法重载和递归类型约束等场景。以下是一些实用的高级写法及示例:

多边界类型参数

当需要对泛型类型参数施加多个约束条件时,可以使用 & 符号连接多个接口(或一个类和多个接口)。注意:类必须放在第一个位置,且只能有一个类。

// 要求T必须同时实现Serializable和Cloneable接口
public static class Custom<T extends Serializable & Cloneable> {private T value;public Custom(T value) {this.value = value;}
}public static class Student implements Serializable, Cloneable {}public static void main(String[] args) {Custom<Student> custom = new Custom<>(new Student());
}

通配符嵌套

在处理复杂泛型结构时,通配符可以嵌套使用,实现更精确的类型约束。

// 嵌套通配符示例:表示一个列表,其元素是另一个列表,内层列表的元素类型是Number或其子类
List<? extends List<? extends Number>> nestedList;// 使用场景:统计嵌套列表中的所有数值之和
public static double sumNestedLists(List<? extends List<? extends Number>> lists) {double sum = 0;for (List<? extends Number> innerList : lists) {for (Number num : innerList) {sum += num.doubleValue();}}return sum;
}

递归类型约束

允许类型参数引用自身,常用于定义 “可比较自身的类型” 或 “自引用接口”。

// 定义一个可排序的元素,要求T必须实现Comparable接口且能比较自身类型
public static class SortedElement<T extends Comparable<T>> {private T value;public SortedElement(T value) {this.value = value;}// 判断当前元素是否小于另一个元素public boolean isLessThan(SortedElement<T> other) {return this.value.compareTo(other.value) < 0;}
}public static void main(String[] args) {SortedElement<Integer> e1 = new SortedElement<>(10);SortedElement<Integer> e2 = new SortedElement<>(20);System.out.println(e1.isLessThan(e2)); // true
}

泛型与函数式接口结合

利用 Java 8+ 的函数式接口和泛型,实现更灵活的类型安全操作。

// 定义一个泛型函数式接口,用于转换类型@FunctionalInterfacepublic interface Converter<T, R> {R convert(T source);}// 泛型工具类:提供类型转换方法public static class GenericConverter {// 使用泛型和函数式接口进行类型转换public static <T, R> List<R> convertList(List<T> sourceList, Converter<T, R> converter) {List<R> result = new ArrayList<>();for (T item : sourceList) {result.add(converter.convert(item));}return result;}}public static void main(String[] args) {List<String> stringList = Arrays.asList("1", "2", "3");List<Integer> integerList = GenericConverter.convertList(stringList, Integer::valueOf);}

泛型数组的创建技巧

由于类型擦除,直接创建泛型数组(如 new T[])是非法的,但可以通过反射创建。

public static class GenericArray<T> {private T[] array;@SuppressWarnings("unchecked")public GenericArray(Class<T> clazz, int size) {// 通过Array.newInstance创建泛型数组this.array = (T[]) Array.newInstance(clazz, size);}public T[] getArray() {return array;}public void set(int index, T value) {array[index] = value;}
}public static void main(String[] args) {GenericArray<String> stringArray = new GenericArray<>(String.class, 3);stringArray.set(0, "Hello");stringArray.set(1, "world");
}

八、注意事项

  • 不能使用基本类型作为泛型参数,如 List是非法的,应使用包装类 List。

  • 不能创建泛型数组,如 new T[10] 是非法的,因为类型擦除后无法知道实际类型。

  • Java 的泛型是通过类型擦除实现的,也就是说,在运行时,泛型信息会被擦除,所有泛型类型最终都变成其上限(通常是 Object)。

  • 不能实例化泛型类型对象,如 new T() 是非法的,因为运行时不知道具体类型。

  • 泛型类型参数无法用于 instanceof 检查

  • 泛型类的静态成员无法使用类的类型参数

总结

Java 泛型通过 “参数化类型” 实现了类型安全与代码复用,T、E、K、V 等符号是约定俗成的类型参数命名,而?通配符则灵活处理了未知类型的场景。

深入理解泛型类、接口、方法的定义与使用,以及 Type 接口的实现类(如 ParameterizedType、TypeVariable),不仅能写出更健壮的代码,还能为理解框架底层的泛型处理(如反射、类型解析)打下基础。

 

今日语录:冲锋吧,爷们!

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

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

相关文章

1688拍立淘接口数据全面解析详细说明(item_search_img)

一、接口概述 1688拍立淘接口是阿里巴巴1688平台提供的基于图像识别的商品搜索服务&#xff0c;允许开发者通过上传商品图片来搜索平台上的同款或相似商品。该接口的主要功能是接收用户上传的图片&#xff08;或图片的相关信息&#xff09;&#xff0c;并通过1688平台的图像识…

【Docker项目实战】使用Docker部署轻量级LetsMarkdown文本编辑器

【Docker项目实战】使用Docker部署轻量级Markdown文本编辑器一、LetsMarkdown介绍1.1 LetsMarkdown简介1.2 主要特点二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3检查docker compose 版本四、拉取容器镜像五…

Node.js自研ORM框架深度解析与实践

Node.js自研ORM框架深度解析与实践 前言 在现代Web开发中&#xff0c;对象关系映射&#xff08;ORM&#xff09;框架扮演着至关重要的角色。它们为开发者提供了一层抽象&#xff0c;使得数据库操作变得更加简单和直观。本文将深入解析一个基于Node.js和MySQL的自研ORM框架&…

汇总图片拖进ps中 photoshop同时打开几个文件夹

如果你有许多文件夹&#xff0c;你想选中一部分&#xff0c;然后把里面的图片全部拖进photoshop当中&#xff0c;但是文件夹又不能直接拖进去&#xff0c;那么你可以尝试使用一下这个工具&#xff0c;首先测试一下直接拖文件夹。选中你要处理的文件夹&#xff0c;直接拖进photo…

mysql 5.7 查询运行时间较长的sql

开发过程遇到sql 执行时间长&#xff0c;又取消不了的情况 可使用 kill query ID 杀死进程获取正在运行的sqlSELECT ID, -- 进程ID&#xff1a;MySQL服务器分配给每个连接的唯一标识符&#xff0c;用于区分不同的客户端连接USER, …

MongoDB 从入门到实践:全面掌握文档型 NoSQL 数据库核心操作

目录 一、MongoDB 基础准备 1. 官方资源获取 2. 安装步骤解析 二、MongoDB 核心指令详解 1. 数据库操作指令 2. 集合操作指令 3. 文档操作指令 查询文档 插入文档 修改文档 删除文档 三、进阶查询技巧 1. 运算符的灵活运用 比较运算符 逻辑运算符 范围与成员运算…

CVPR2025丨遥感领域,全模态与秒超高清遥感建模重大突破,性能提升创新点

关注gongzhonghao【CVPR顶会精选】刚入门遥感建模时&#xff0c;总好奇别人为什么总能提出新方法&#xff1f;慢慢摸索后才发现&#xff0c;创新点并不是硬憋出来的&#xff0c;而是要从数据特性、传感器差异、地物细节以及环境变化中发现机会。不同波段、不同分辨率、不同时相…

HTML5详篇

前端三剑客 前端三剑客是指HTML、CSS和JavaScript: HTML超文本标记语言(Hyper Text Markup Language):简单理解描述网页结构的;用于网页内容的语言。它通过使用不同的HTML标签来定义页面中的各种元素,例如标题、段落、图像、链接等【无羽毛的小鸟模型】 CSS层叠样式表(…

【Transient-Free 3DGS】delayed densification + coarse to fine增加GS的鲁棒性

25年最新连接去除场景瞬态对象工程与3DGS的pipeline&#xff0c;改进了spotlesssplats&#xff0c;已开源&#xff1a; [2506.02751] RobustSplat: Decoupling Densification and Dynamics for Transient-Free 3DGSAbstract page for arXiv paper 2506.02751: RobustSplat: De…

【MySQL】CRUD基础详解

CRUD基础前言&#xff1a;数据库的层级结构一、新增&#xff08;Create&#xff09;1. 单行数据 全列插入2. 单行数据的简写插入3. 指定列插入4. 多行数据插入二、查询&#xff08;Retrieve&#xff09;1. 全列查询2. 指定列查询3. 查询结果为表达式&#xff08;1&#xff09;…

互联网大厂Java求职面试实录:核心技术栈与业务场景解析

互联网大厂Java求职面试实录&#xff1a;核心技术栈与业务场景解析 面试场景设定 本文通过一个严肃的面试官和搞笑的水货程序员大面条之间的对话&#xff0c;模拟互联网大厂Java岗位的技术面试过程。面试涵盖Java SE、Spring生态、数据库、微服务、缓存、安全、消息队列、AI等多…

response对象的elapsed属性

在Python的requests库中&#xff0c;当我们发送一个请求后&#xff0c;会得到一个Response对象&#xff0c;这个对象有一个elapsed属性&#xff0c;它返回一个timedelta对象&#xff0c;表示从发送请求到收到响应所经过的时间。response.elapsed.total_seconds() 是 Python req…

【ansible】5.在受管主机部署文件和Jinja2模板

1.Ansible 中&#xff0c;如何用模块创建一个文件并设置权限644并设置SELinux类型&#xff0c;如何从受管主机中删除文件&#xff1f;使用ansible.builtin集合中的 file 模块&#xff0c;添加state&#xff1a;touch 创建文件&#xff0c;mode&#xff1a;‘0644’ 设置权限&am…

雪花算法数据库主键

雪花算法&#xff08;Snowflake&#xff09;作为一种分布式 ID 生成方案&#xff0c;在分布式系统中具有显著优势&#xff0c;能够解决多个关键问题。以下是它的核心好处及主要应用场景&#xff1a;雪花算法的核心好处全局唯一性&#xff1a;通过时间戳、机器 ID、数据中心 ID …

C/C++ 头文件命名约定

有的时候&#xff0c;在C的代码中&#xff0c;可以看到有如下的头文件引用的代码: #include <iostream> #include <unistd.h> #include <csignal>其中有一些是引用了.h文件&#xff0c;另外一些是引用了模块式的比如iostream和csignal&#xff0c;那么为什么…

异质结3.0时代的降本提效革命:捷造科技设备技术创新与产业拐点分析

光伏产业经历了从PERC到TOPCon和异质结&#xff08;HJT&#xff09;的技术迭代&#xff0c;而2025年将成为异质结技术规模化应用的关键转折点。捷造科技通过一系列突破性技术创新&#xff0c;将GW级异质结整线设备价格降至2亿元&#xff0c;较行业平均水平降低约40%&#xff0c…

【网络】http 协议中 Vary 标头的作用

在 HTTP 协议中&#xff0c;Vary 标头是一个关键的缓存控制机制&#xff0c;用于告知缓存服务器&#xff08;或代理&#xff09;&#xff1a;响应内容的生成依赖于请求中的哪些特定头部字段。其核心作用是确保缓存服务器能根据这些字段的差异&#xff0c;正确区分和返回不同版本…

CSS 进阶用法

一、选择器进阶复杂选择器组合详解后代选择器后代选择器使用空格分隔两个选择器&#xff0c;例如div p&#xff0c;表示选择div元素内所有的p元素。这种选择方式会匹配所有层级的后代元素&#xff0c;包括子元素、孙元素等任意深度的嵌套元素。应用示例&#xff1a;/* 选中arti…

GitHub 热榜项目 - 日榜(2025-08-23)

GitHub 热榜项目 - 日榜(2025-08-23) 生成于&#xff1a;2025-08-23 统计摘要 共发现热门项目&#xff1a;13 个 榜单类型&#xff1a;日榜 本期热点趋势总结 本期GitHub热榜呈现三大技术热点&#xff1a;1&#xff09;AI工作流构建成为风口&#xff0c;sim和airi等项目展示…

SHAP分析+KOA-RIME开普勒结合霜冰算法双重优化BP神经网络+9种映射方法+新数据预测!机器学习可解释分析!

代码主要功能 该Matlab代码实现了一个KOA-RIME开普勒结合霜冰算法双重优化的BP神经网络回归模型&#xff0c;结合特征贡献度分析&#xff08;SHAP&#xff09;和新数据预测功能。核心功能包括&#xff1a; 双重参数优化&#xff1a;先用智能算法&#xff08;以chebyshev映射改进…