java 泛型
java 泛型转换的事实:
- 虚拟机中没有泛型, 只有普通的类和方法: 因为存在 类型擦除, 泛型类型被替换为限定类型(无限定类型就使用 Object), 限定类型是指
<T extends ClassName & InterfaceName & XXX>
; - 所有的类型参数都用它们的限定类型替换;
- 编译器会生成 桥方法 以保持多态;
- 为保持类型安全性, 必要时插入强制类型转换;
java 泛型的约束和局限性:
1. 不能使用基本类型实例化类型参数;
2. 运行时类型查询只适用于原始类型;
因为 类型擦除, java 虚拟机中的对象没有泛型类型这一说, instanceof
和 getClass()
只能查询到原始类型, 具体的泛型类型时无从得知的.
3. 不能创建参数化类型的数组
只可声明参数化类型的数组, 但不能创建参数化类型的数组.
例如 Pair<String> table = new Pair<String>[10]
是错误的,
4. Varargs 警告
虽然不支持创建泛型类型的数组, 但由于可变参数其实是个数组, 当向可变参数化传递泛型类型时, 就会有这个问题.
为此 java 编译器支持了这种情况, 但还会产生一个警告, 可通过 @SafeVarargs
注解抑制该警告.
5. 不能实例化类型变量
不能直接通过 new
实例化类型变量(因为 类型擦除 后会变成 Object
类型), 这时候可借助函数式接口 Supplier<T>
,
表示一个无参数且返回类型为 T 的函数:
public static <T> Pair<T> makePair<Supplier<T> constr) {
return new Pair<>(constr.get(), constr.get());
}
Pair<String> pair = Pair.makePair(String.class);
6. 不能构造泛型数组
7. 泛型类的静态上下文类型变量无效
8. 不能抛出或捕获泛型类的实例
9. 可以消除对受查异常的检测
10. 注意类型擦除后的冲突
要想支持擦除的转换, 就需要强行限制一个类或类型变量不能同时成为两个接口类型的子类, 而这两个接口是同一接口的不同参数化. 例如下述代码是非法的:
class Employee implements Comparable<Employee> { ... }
class Manager extends Employee implements Comparable<Manager> { ... }
Manager
会实现 Comparable<Employee>
和 Comparable<Manager>
, 这是同一接口的不同参数化.
但, 非泛型版本是合法的:
class Employee implements Comparable { ... }
class Manager extends Employee implements Comparable { ... }
泛型里的通配符概念
当知道泛型的具体类型时, 使用字符 T
(当然其他字符也行).
但当泛型的参数类型变化时, 就只能使用 ?
了. 其中:
<? extends 类或接口>
: 子类型限定, 只可从泛型对象读取, 不能写入;<? super 类或接口>
: 超类型限定, 可向泛型对象写入, 读取时不能保证类型(只能是 Object).