Effective-Java-28列表优于数组
数组与泛型相比,有两个重要的不同点。首先,数组是协变的(covariant)。这个词听起来有点吓人,其实只是表示,如果Sub为Super的子类型,那么数组类型Sub[]就是Super[]子类型。[1]相反,泛型则是可变的(invariant):对于任意两个不同的类型Type1和Type2,List
1 | // Fails at runtime! |
但下面这段代码则不合法
1 | // Won't compile! |
这其中无论哪一种方法,都不能将 String 放进 Long 容器中,但是使用数组,你会在运行时才发现所犯的错误;而使用列表,则可以在编译时就发现错误。[3]
数组与泛型的第二大区别:数组是具象化的 (reified)[JLS, 4.7]。因此数组会在运行时知道和强化它们的元素类型。如上所述,将 String 保存到 Long 数组中,就会得到一个 ArrayStoreException 异常。[4]泛型则是通过擦除(erasure)[JLS, 4.6]来实现的。**泛型只会在编译时强化它们的类型信息,运行时丢弃(或者擦除)它们的元素信息。**擦除就是使泛型可以与没有使用泛型的代码随意进行互用(见26条),以确保在Java5中平滑过渡到泛型。[5]
因为以上这些根本的区别,数组和泛型不能很好地混合使用。例如,创建泛型、参数化类型或者类型参数的数组是非法的。[6]
以下数组创建表达式没有一个是合法的:
这些在编译期会产生一个泛型数组创建(generic array creation)错误。
1 | new List<String>[]; |
从技术角度上来说,E、List
Java Language Specification SE 21, §4.10.3 Subtyping among Array Types, defines array subtyping in terms of component-type subtyping. ↩︎
Joshua Bloch, Effective Java, 3rd Edition, Item 28, uses this contrast between arrays and generic lists to motivate preferring lists in generic APIs. ↩︎
Java Language Specification SE 21, §10.5 Array Store Exception, specifies the runtime store check that throws
ArrayStoreExceptionwhen a value is not assignment-compatible with the array’s actual component type. ↩︎Java Language Specification SE 21, §4.7 Reifiable Types, explains which types are fully available at runtime; arrays are reifiable only when their component type is reifiable. ↩︎
Java Language Specification SE 21, §4.6 Type Erasure, defines how parameterized types and type variables are erased. ↩︎
Java Language Specification SE 21, §15.10.1 Array Creation Expressions, makes array creation with a non-reifiable component type a compile-time error. ↩︎
Java Language Specification SE 21, §4.7 Reifiable Types, identifies parameterized types other than unbounded wildcard forms as non-reifiable. ↩︎