发布于2026-05-23 阅读(0)
扫一扫,手机访问

想用一套代码处理多种数据类型,同时让编译器帮你把好类型安全关?泛型类就是为此而生的。它的核心思路很巧妙:把“数据类型”本身变成一个可以传递的参数。这样一来,你只需编写一次通用逻辑,就能安全地复用于各种类型,从根源上避免了强制类型转换的繁琐和潜在的类型转换错误。
定义泛型类的第一步,是在类名后面加上一个尖括号,里面放一个类型占位符,比如
这个占位符 T 就像是一个“类型变量”,你可以在类的成员变量、方法参数或返回值中直接使用它:
private T data; 或 public void set(T value) 这样的写法。doubleValue() 这类 Number 的共通方法了。定义好泛型类之后,真正发挥威力是在创建对象的时候。这时,你需要为那个占位符 T 填入一个具体的引用类型。
这个动作就像给编译器下达了一道明确的指令:
Box strBox = new Box<>(); ,编译器就知道,这个 strBox 实例只允许存放 String 对象。从它里面取出来的数据,直接就是 String 类型,无需任何强制转换。Box intBox = new Box<>(); 声明的盒子就只能存整数。如果你试图往里放一个 "abc",编译器会在第一时间报错,将类型错误扼杀在摇篮里。Box 这样的写法是不合法的,但你可以使用其对应的包装类 Integer。泛型类本身已经很强大了,但当你的方法需要接收或返回一个“泛型容器”时,可能会遇到类型过于僵化的问题。比如,一个处理 List 的方法,竟然无法接受 List 作为参数。
这时,通配符 ? 就该登场了。它能在保持类型安全的前提下,提供必要的灵活性。关键在于,要根据数据的“流向”(读或写)来选择合适的通配符:
printAll(List extends Number> list),那么你既可以传入 List,也可以传入 List。不过,由于编译器无法确定容器的具体元素类型,除了 null 之外,你不能向这个列表中添加任何元素,但可以安全地读取其中的 Number。addIntegers(Collection super Integer> coll) 方法可以接受 Collection 甚至 Collection。你可以安全地向其中写入 Integer 对象,因为 Integer 总是可以向上转型为它的父类。但从里面读取元素时,你只能得到 Object 类型。 extends Object>。它的使用范围最窄,通常只能调用那些与类型无关的方法,比如 size() 或 clear(),既不能读也不能写具体类型的数据。简单来说,遵循“PECS”原则(Producer-Extends, Consumer-Super)能让你的API既灵活又安全。
Ja va 泛型有一个重要的设计特点:类型擦除。这意味着,泛型类型信息只在编译期存在,用于严格的类型检查。到了运行时,所有泛型参数都会被擦除,替换为其原始类型(如 Object)或上界类型。
这个机制带来了性能上的优势,但也导致了一些操作无法直接进行:
if (t instanceof T) 这样的代码是无法编译的,因为运行时 T 已经不存在了。new T() 会编译失败。常见的解决方案是传入一个 Class 对象并使用 newInstance(),或者传入一个 Supplier 函数式接口。public static E getFirst(List list) 。理解并妥善处理这些限制,是熟练运用 Ja va 泛型的关键一步。说到底,泛型是在编译期为你编织的一张类型安全网,而类型擦除则是为了兼容性而付出的必要代价。用好它,能让你的代码既简洁又健壮。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8