引言:泛型解决的问题
想象一下,您正在编写一个对数字列表进行排序的方法。您为Integer编写了它,但一周后您需要对String列表进行排序。您必须创建一个新方法,或者使用Object并不断进行类型转换。这很不方便,不安全,并且会导致运行时错误。
正是这个问题由Java中的泛型解决。它们允许创建处理不同数据类型的类、接口和方法,同时在编译阶段保持严格的类型检查。没有泛型,现代Java将远没有那么方便和安全。
在本文中,我们将探讨为什么需要泛型,它们是如何工作的,以及最重要的是,如何在实际示例中应用它们。
1. 泛型基础:它是什么以及如何工作
1.1. “原始”类型的问题
在Java 5(2004年)出现之前,开发人员使用未指定类型的集合:
List list = new ArrayList();list.add("Hello");list.add(123);// 获取时需要记住存储的类型String text = (String) list.get(0); // 正常Integer num = (Integer) list.get(1); // 正常// 但如果弄错了——运行时出现ClassCastExceptionString error = (String) list.get(1); // 错误!这样的代码很危险:错误只有在程序运行时才会被发现。
1.2. 泛型如何工作
泛型允许您在尖括号<T>中直接指定类或方法将要处理的数据类型。编译器在编译阶段检查类型,并自动插入正确的类型转换。
基本语法:
// 声明泛型类public class Box<T> { private T content; public void setContent(T content) { this.content = content; } public T getContent() { return content; }}
// 使用Box<String> stringBox = new Box<>(); // <> — 菱形运算符 (Java 7+)stringBox.setContent("你好");String text = stringBox.getContent(); // 无需类型转换!
Box<Integer> intBox = new Box<>();intBox.setContent(42);int number = intBox.getContent(); // 无需类型转换!
// stringBox.setContent(123); // 编译错误!1.3. 实践中的优势
- 类型安全:错误在编译阶段被检测到,而不是在运行时。
- 消除显式类型转换:代码变得更简洁、更易读。
- 代码复用:一个类/方法可以处理任何类型。
2. 实践中的泛型:类、方法和接口
2.1. 泛型类
最常见的情况是集合。但您可以创建自己的泛型类:
public class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; }}
// 使用Pair<String, Integer> pair = new Pair<>("年龄", 30);String key = pair.getKey();Integer value = pair.getValue();2.2. 泛型方法
方法可以拥有自己的类型参数,与类无关:
public class Utils { // 将数组转换为列表的泛型方法 public static <T> List<T> arrayToList(T[] array) { List<T> list = new ArrayList<>(); for (T element : array) { list.add(element); } return list; }}
// 使用String[] names = {"安娜", "鲍里斯", "维克多"};List<String> nameList = Utils.arrayToList(names);