Java Generics
拓展 Java 类型系统,允许对各类型对象进行操作,提供编译时类型安全;
Motivation1 / 问题代码 1
final List v = new ArrayList();
v.add("test"); // A String that cannot be cast to an Integer
final Integer i = (Integer) v.get(0); // Run time error
Better way on java05
final List<String> v = new ArrayList<String>();
v.add("test");
final Integer i = (Integer) v.get(0); // (type error) compilation-time error
Better type inference
(类型推断) on java07
final Entry<String, String> grade = new Entry<>("Mike", "A");
final Entry<String, Integer> mark = new Entry<>("Mike", 100);
System.out.println("grade: " + grade);
System.out.println("mark: " + mark);
final Entry<Integer, Boolean> prime = new Entry<>(13, true);
if (prime.getValue()) System.out.println(prime.getKey() + " is prime.");
else System.out.println(prime.getKey() + " is not prime.");
Type wildcards (类型通配符)
final Collection<?> c = new ArrayList<String>();
c.add(new Object()); // compile-time error
c.add(null); // allowed
问题代码 2
final List<Integer> ints = new ArrayList<>();
ints.add(2);
final List<Number> nums = ints; // valid if List<Integer> were a subtype of List<Number> according to substitution rule.
nums.add(3.14);
final Integer x = ints.get(1); // now 3.14 is assigned to an Integer variable!
规定上限 ⇒ extends
⇒ 保证读取安全,但限制了写操作 (只能存入)
final List<? extends Number> nums = ints; // OK
nums.add(3.14); // compile-time error
nums.add(null); // allowed
规定下限 ⇒ super
⇒ Number 的任何子类 ⇒ 保证了写操作,但限制了读操作
List<? super Integer> list = new ArrayList<Number>();
list.add(1); // OK
list.add(2); // OK
Number n = list.get(0); // 编译错误
Integer i = list.get(0); // 编译错误
Object o = list.get(0); // OK
Type erasure (类型擦除)
final List<Integer> li = new ArrayList<>();
final List<Float> lf = new ArrayList<>();
if (li.getClass() == lf.getClass()) { // evaluates to true
System.out.println("Equal");
}
- 以上代码片段将输出
Equal
,类型检查只会发生在编译期,运行时的代码无法判断泛型的具体内容; - 不支持
extends Throwable
,当走到 catch 块之后,无法判断进入到哪一分支
try {
throw new GenericException<Integer>();
} catch (GenericException<Integer> e) {
System.err.println("Integer");
} catch (GenericException<String> e) {
System.err.println("String");
}
- 不支持实例化泛型,如下述代码将无法通过编译
<T> T instantiateElementType(List<T> arg) {
return new T(); //causes a compile error
}
Vs Array (比较于数组)
- 因为类型擦除,运行时的数组的类型信息要比泛型更加具体;
-
Arrays are reified, meaning that an array object enforces its type information at run-time, whereas generics in Java are not reified.
Reference
- 泛型和反射 - 廖雪峰
- 我们不能直接创建泛型数组
T[]
,因为擦拭后代码变为Object[]
- 带泛型的数组实际上是编译器的类型擦除
- 我们不能直接创建泛型数组