數組的協變性(covariant)是指:
如果類Base是類Sub的基類,那麼Base[]就是Sub[]的基類。
而泛型是不可變的(invariant),List<Base>不會是List<Sub>的基類,更不會是它的子類。
數組的協變性可能會導致一些錯誤,比如下面的代碼:
public static void main(String[] args) {
Object[] array = new String[10];
array[0] = 10;
}
它是可以編譯通過的,因為數組是協變的,Object[]類型的引用可以指向一個String[]類型的對象
但是運行的時候是會報出如下異常的:
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
但是對於泛型就不會出現這種情況了:
public static void main(String[] args) {
List< Object> list = new ArrayList< String>();
list.add(10);
}
這段代碼連編譯都不能通過。
數組是具體化的(reified),而泛型在運行時是被擦除的(erasure)。
數組是在運行時才去判斷數組元素的類型約束,
而泛型正好相反,在運行時,泛型的類型信息是會被擦除的,只有編譯的時候才會對類型進行強化。
所以上面的例子中,數組的方法會在運行時報出ArrayStoreException,而泛型根本無法通過編譯。
雖然將集合看作是數組的抽象會有所幫助,但是數組還有一些集合不具備的特殊性質。
Java 語言中的數組是協變的(covariant),也就是說,如果 Integer擴展了 Number(事實也是如此),那麼不僅 Integer是 Number,而且 Integer[]也是 Number[],在要求 Number[]的地方完全可以傳遞或者賦予 Integer[]。(更正式地說,如果 Number是 Integer的超類型,那麼 Number[]也是 Integer[]的超類型)。
您也許認為這一原理同樣適用於泛型類型 —— List<Number>是 List<Integer>的超類型,那麼可以在需要 List<Number>的地方傳遞 List<Integer>。不幸的是,情況並非如此。
不允許這樣做有一個很充分的理由:
這樣做將破壞要提供的類型安全泛型。
如果能夠將 List<Integer>賦給 List<Number>。
那麼下面的代碼就允許將非 Integer的內容放入 List<Integer>
List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));
因為 ln是 List<Number>,所以向其添加 Float似乎是完全合法的。但是如果 ln是 li的別名,那麼這就破壞了蘊含在 li定義中的類型安全承諾 —— 它是一個整數列表,這就是泛型類型不能協變的原因。
Java 8簡明教程 http://www.linuxidc.com/Linux/2014-03/98754.htm
Java對象初始化順序的簡單驗證 http://www.linuxidc.com/Linux/2014-02/96220.htm
Java對象值傳遞和對象傳遞的總結 http://www.linuxidc.com/Linux/2012-12/76692.htm
Java對象序列化ObjectOutputStream和ObjectInputStream示例 http://www.linuxidc.com/Linux/2012-08/68360.htm