泛型
泛型,又称参数化类型
泛型类并没有自己独有的 Class
类对象。比如:并不存在 List<Object>.class
或是 List<Interger>.class
,Java 编译器会将二者都视为 List.class
。
泛型一些约定俗成的命名:
- E - Element
- K - Key
- N - Number
- T - Type
- V - Value
- T - 类型,主要用于表示第一类通用型参数。
S - 类型,主要用于表示第二类通用类型参数。
U - 类型,主要用于表示第三类通用类型参数。
V - 类型,主要用于表示第四个通用类型参数。
1 泛型优点
泛型具有以下优点:
-
编译时的强类型检查
-
泛型要求在声明时指定实际数据类型,Java 编译器在 编译时会对泛型代码做强类型检查 ,并在代码违反类型安全时发出警告。在编译时发现并修复错误所付出的代价远比在运行时小。
-
消除强制类型转换
- 未使用泛型:
List list = new ArrayList(); list.add("hello"); String s = (String) list.get(0);
- 使用泛型:
List<String> list = new ArrayList<String>(); list.add("hello"); String s = list.get(0); // no cast
-
泛型编程可以实现通用算法
- 通过使用泛型,程序员可以实现通用算法,这些算法可以处理不同类型的集合,可以自定义,并且类型安全且易于阅读。
2 自动装箱
类型参数必须被实例化为引用类型,因此Java有一种特殊机制来使泛型代码能够处理原始数据类型。
自动将一个原始数据类型转换为一个封装类型被称为自动装箱,自动将一个封装类型转换为一个原始数据类型被称为自动拆箱。
Stack<Integer> stack = new Stack<Integer>();
stack.push(17) ; // 自动装箱(int -> Integer)
int i = stack.pop(); // 自动拆箱(Integer -> int)
3 类型擦除
类型擦除做了以下工作:
- 把泛型中的所有类型参数替换为 Object,如果指定类型边界,则使用类型边界来替换。因此,生成的字节码仅包含普通的类,接口和方法。
- 擦除出现的类型声明,即去掉
<>
的内容。比如T get()
方法声明就变成了Object get()
;List<String>
就变成了List
。如有必要,插入类型转换以保持类型安全。 - 生成桥接方法以保留扩展泛型类型中的多态性。类型擦除确保不为参数化类型创建新类;因此,泛型不会产生运行时开销。
4 java 不支持泛型数组
-
为什么 java 不支持泛型数组?
-
共变数组(covariant array)
-
数组的协变性(covariant)是指:
如果类Base是类Sub的基类,那么Base[]就是Sub[]的基类。
而泛型是不可变的(invariant),List< Base >不会是List< Sub >的基类,更不会是它的子类。
-
-
类型擦除(type erasure)
- 数组是具体化的 ( reified ),而泛型在运行时是被擦除的 ( erasure )。
- 数组是在运行时才去判断数组元素的类型约束,
- 而泛型正好相反,在运行时,泛型的类型信息是会被擦除的,只有编译的时候才会对类型进行强化。
-
5 类型通配符
上界通配符 语法形式:<? extends Number>
下界通配符 语法形式:<? super Number>
无界通配符 语法形式:<?>
6 泛型约束
- 泛型类型的类型参数不能是值类型
Pair<int, char> p = new Pair<>(8, 'a'); // 编译错误
- 不能创建类型参数的实例
public static <E> void append(List<E> list) {
E elem = new E(); // 编译错误
list.add(elem);
}
- 不能生命类型为类型参数的静态成员
public class MobileDevice<T> {
private static T os; // error
// ...
}
- 参数类型不能使用类型转换或 instanceof
public static <E> void rtti(List<E> list) {
if (list instanceof ArrayList<Integer>) { // 编译错误
// ...
}
}
List<Integer> li = new ArrayList<>();
List<Number> ln = (List<Number>) li; // 编译错误
- 不能创建参数类型的数组
List<Integer>[] arrayOfLists = new List<Integer>[2]; // 编译错误
- 不能创建、catch 或 throw 参数化类型对象
// Extends Throwable indirectly
class MathException<T> extends Exception { /* ... */ } // 编译错误
// Extends Throwable directly
class QueueFullException<T> extends Throwable { /* ... */ // 编译错误
public static <T extends Exception, J> void execute(List<J> jobs) {
try {
for (J job : jobs)
// ...
} catch (T e) { // compile-time error
// ...
}
}
- 仅仅是泛型类相同,而参数类型不同的方法不能重载
public class Example {
public void print(Set<String> strSet) { }
public void print(Set<Integer> intSet) { } // 编译错误
}