什么是泛型
约 740 字大约 2 分钟
2025-07-17
1.1 泛型的引入
在 Java 标准库中,ArrayList
是一种可以动态调整大小的数组,它内部基于 Object[]
数组实现。
public class ArrayList {
private Object[] array;
private int size;
public void add(Object e) {...}
public void remove(int index) {...}
public Object get(int index) {...}
}
如果使用上述 ArrayList
存储 String
类型的数据,会存在以下缺点:
- 需要强制类型转换;
- 容易出错,例如,可能会出现
ClassCastException
异常。
例如,从 ArrayList
中获取 String
类型的数据时,需要进行强制类型转换:
ArrayList list = new ArrayList();
list.add("Hello");
// 获取到Object,必须强制转型为String:
String first = (String) list.get(0);
为了解决这个问题,可以为每种类型单独编写一个 ArrayList
,例如 StringArrayList
、IntegerArrayList
等。但是,这种方式会导致大量的重复代码,且无法应对所有可能的类型。
1.2 泛型的定义
为了解决上述问题,Java 引入了泛型的概念。泛型允许定义一种模板,例如 ArrayList<T>
,其中 T
可以是任何 class。
public class ArrayList<T> {
private T[] array;
private int size;
public void add(T e) {...}
public void remove(int index) {...}
public T get(int index) {...}
}
通过泛型,可以创建任意类型的 ArrayList
:
// 创建可以存储String的ArrayList:
ArrayList<String> strList = new ArrayList<String>();
// 创建可以存储Float的ArrayList:
ArrayList<Float> floatList = new ArrayList<Float>();
// 创建可以存储Person的ArrayList:
ArrayList<Person> personList = new ArrayList<Person>();
这样,既实现了代码的复用,又通过编译器保证了类型安全。
1.3 泛型的类型检查
编译器会对泛型类型进行检查,确保类型安全。
ArrayList<String> strList = new ArrayList<String>();
strList.add("hello"); // OK
String s = strList.get(0); // OK
strList.add(new Integer(123)); // compile error!
Integer n = strList.get(0); // compile error!
1.4 向上转型
在 Java 标准库中,ArrayList<T>
实现了 List<T>
接口,因此,类型 ArrayList<T>
可以向上转型为 List<T>
。
public class ArrayList<T> implements List<T> {
...
}
List<String> list = new ArrayList<String>();
需要特别注意的是,不能把 ArrayList<Integer>
向上转型为 ArrayList<Number>
或 List<Number>
。
这是因为如果允许这种转型,可能会导致类型安全问题。例如,可以将一个 Float
对象添加到 ArrayList<Number>
中,而 ArrayList<Integer>
实际上只能存储 Integer
对象,这会导致 ClassCastException
异常。
// 创建ArrayList<Integer>类型:
ArrayList<Integer> integerList = new ArrayList<Integer>();
// 添加一个Integer:
integerList.add(new Integer(123));
// “向上转型”为ArrayList<Number>:
ArrayList<Number> numberList = integerList;
// 添加一个Float,因为Float也是Number:
numberList.add(new Float(12.34));
// 从ArrayList<Integer>获取索引为1的元素(即添加的Float):
Integer n = integerList.get(1); // ClassCastException!
因此,编译器为了避免这种错误,根本就不允许把 ArrayList<Integer>
转型为 ArrayList<Number>
。
注
ArrayList<Integer>
和 ArrayList<Number>
两者完全没有继承关系。
泛型的继承关系可以用下图表示:当 T
不变时,可以向上转型;T
本身不能向上转型。
List<Integer> ArrayList<Number>
▲ ▲
│ │
│ X
│ │
ArrayList<Integer> ArrayList<Integer>