Effective Java(Item 26)Prefer List to Arrays
Generics Covariant Invariant Reification Erasure Contravariant
Prefer List to Arrays
这里总结以下Java范型的一些重要概念,并尝试通过分析语言设计者的意图,加深对Java语言类型系统的理解,并对比下Kotlin。
Java 从1.5版本开始引入范型的概念。在引入范型之前,从集合中读出的对象,都要手动cast,如果类型不一致,就会导致运行时的异常。而通过范型,我们可以告诉编译器集合中允许的类型。编译器自动插入cast,并在编译时报错,如果你插入了错误的类型。有范型加持的程序,更安全更简洁,当然也更复杂。
Covariant vs Invariant vs Contravariant
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dst.set(i, src.get(i));
}
}
Covariant
type S[] is a subtype of T[] whenever S is a subtype of T
Invariant
type List<”S”> is not a subtype of List<”T”> when S is a subtype of T
Contravariant
type List<”S”> is a subtype of List<? super T> when S is a supertype of T
Wildcards
- The Get and Put Principle
use extend wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure, and don’t use a wildcard when you both get and put. - wildcard capture
When a generic method is invoked, the type parameter may be chosen to match the unknown type represented by a wildcard.public static void reverse(List<?> list) { List<Object> tmp = new ArrayList<Object>(list); for (int i = 0; i < list.size(); i++) { list.set(i, tmp.get(list.size()-i-1)); //compile-time error }}
- in & out in Kotlin
- in for contravariant
interface Comparable<in T> { operator fun compareTo(other: T): Int } fun demo(x: Comparable<Number>) { x.compareTo(1.0) // 1.0 has type Double, which is a subtype of Number // Thus, you can assign x to a variable of type Comparable<Double> val y: Comparable<Double> = x // OK! }
- out for covariant
interface Source<out T> { fun nextT(): T } fun demo(strs: Source<String>) { val objects: Source<Any> = strs // This is OK, since T is an out-parameter // ... }
Reification vs Erasure
Java中, 数组reify内容的信息,而范型不会reify他们的类型参数。 我们说一个类型reifiable,如果它在运行时和编译时完全一样,也就是erasure没有去除任何有用的信息。
- 下面的类型都reifiable
- 基础类型 (int)
- 非参数化的类或者接口 (Number, String, Runnable)
- 参数类型,但所有的类型参数是无边界匹配 (List<?>)
- raw 类型 (List, ArrayList, Map)
- 内容类型都是reifiable的数组(int[], Number[], List<?>[], List[], int[][])
- 以下类型都是不能reifiable的
- 类型变量 (T)
- 有具体参数的参数化类型 (List<’String’>)
Erasure
- 为什么用erasure来实现范型
- 简单,只需要改编译器,保持二进制兼容
- 方便逐渐的进化,让新代码用范型的同时,与老代码兼容
Arrays vs Generics
数组和范型是非常不同的类型规则。数组是covariant和reified;范型是invariant和erased。