泛型实现了参数化类型的概念。
根据泛型应用的不同,可分为:
泛型类
1
2
3class ClassParameter<T> {
public T f(T arg) { return arg; }
}泛型接口
1
2
3interface InterfaceParameter<T> {
T f(T arg);
}泛型方法
1
2
3class MethodParameter {
public static <T> T f(T arg) { return arg; }
}
泛型的擦除
1 | public class ErasedTypeEquivalence { |
Java泛型是使用擦除来实现的,这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。因此,List<String>
和List<Integer>
在运行时事实上是相同的类型。这两种形式都被擦除成它们的原生类型,即List
。
在基于擦除的实现中,泛型类型被当作第二类类型处理,即不能在某些重要的上下文环境中使用的类型。
泛型类型只有在静态类型检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换为它们的非泛型上界。
擦除的代价是显著的,泛型不能用于显示地引用运行时类型的操作之中,例如转型、instanceof
操作和new
表达式。
问题
在使用Java泛型时会出现的各类问题。
- 任何基本类型都不能作为类型参数。
解决办法是使用基本类型的包装器类及自动装箱机制。ArrayList<Integer>
- 实现参数化接口
一个类不能实现同一个泛型接口的两种变体,由于擦除的原因,这两种变体会成为相同的接口。
1 | interface Payable<T> {} |
- 转型和警告
使用带有泛型类型参数的转型或instanceof不会有任何效果。
- 重载
1 | public class UseList<W,T> { |
由于擦除的原因,重载方法将产生相同的类型签名。
当被擦除的参数不能产生唯一的参数列表时,必须提供明显有区别的方法名:
1 | public class UseList2<W,T> { |
- 基类劫持了接口
1 | class Pet implements Comparable<Pet> { |
1 | class Cat extends ComparablePet implements Comparable<Cat> { |
数组与泛型
不能实例化具有参数化类型的数组:
1 | Peel<Banana>[] peels = new Peel<Banana>[10]; //非法 |
擦除会移除参数类型信息,而数组必须知道它们所持有的确切类型,以强制保证类型安全。
但是,可以参数化数组本身的类型:
1 | class ClassParameter<T> { |