Java 中的泛型:为何需要以及如何使用
泛型是 Java 语言的关键特性之一,自 J2SE 5.0 版本引入。它允许你创建适用于不同数据类型的类、接口和方法,同时在编译时保持强类型检查。在本文中,我们将探讨为何需要泛型、如何正确使用它们,以及它们能帮助避免哪些错误。
为何 Java 中需要泛型?
在泛型出现之前,程序员需要使用类型转换和操作 Object 类型。这导致了两个主要问题:
- 缺乏类型安全性 — 任何类型的对象都可以被添加到集合中,错误只会在运行时出现。
- 需要显式类型转换 — 从集合中获取元素时需要进行强制类型转换,使代码变得臃肿。
// 没有泛型(Java 5 之前)List list = new ArrayList();list.add("string");list.add(123); // 编译时没有错误String s = (String) list.get(0); // 必须显式转换// 使用泛型List<String> list = new ArrayList<>();list.add("string");// list.add(123); // 编译错误!String s = list.get(0); // 不需要类型转换泛型的主要优势:
- 类型安全性 — 类型错误在编译时就被检测出来。
- 消除显式类型转换 — 代码更简洁、更易读。
- 代码可复用性 — 同一实现可适用于不同类型。
创建泛型类和方法
泛型类
要创建适用于任意类型的类,只需在类名后的尖括号中指定类型参数:
public class Box<T> { private T content;
public void setContent(T content) { this.content = content; }
public T getContent() { return content; }
public static void main(String[] args) { Box<String> stringBox = new Box<>(); stringBox.setContent("Hello, Java!"); System.out.println(stringBox.getContent());
Box<Integer> intBox = new Box<>(); intBox.setContent(42); System.out.println(intBox.getContent()); }}在这个例子中,T 是类型参数。当创建 Box<String> 对象时,所有出现 T 的地方都会被替换为 String。
泛型方法
方法也可以被参数化。类型参数在返回类型之前指定:
public class Util { public static <T> void printArray(T[] array) { for (T element : array) { System.out.print(element + " "); } System.out.println(); }
public static void main(String[] args) { Integer[] intArray = {1, 2, 3, 4, 5}; String[] strArray = {"A", "B", "C"};
printArray(intArray); // 1 2 3 4 5 printArray(strArray); // A B C }}注意:类型参数 <T> 位于 void 之前。这是泛型方法的强制要求。
有界类型参数
有时需要限制哪些类型可以用作参数。关键词 extends 和 super 用于此目的。
上界(extends)
从上方限制类型——只能使用指定的类本身或其子类:
public class Calculator<T extends Number> { private T value;
public Calculator(T value) { this.value = value; }
public double square() { return va