共變數組和類型擦除 java

1、數組的協變性

數組的協變性(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); 
}

這段代碼連編譯都不能通過。

2、數組的具體化。

數組是具體化的(reified),而泛型在運行時是被擦除的(erasure)。

數組是在運行時纔去判斷數組元素的類型約束,

而泛型正好相反,在運行時,泛型的類型信息是會被擦除的,只有編譯的時候纔會對類型進行強化。

所以上面的例子中,數組的方法會在運行時報出ArrayStoreException,而泛型根本無法通過編譯。

 

3、泛型不是協變的

 

雖然將集合看作是數組的抽象會有所幫助,但是數組還有一些集合不具備的特殊性質。

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定義中的類型安全承諾 —— 它是一個整數列表,這就是泛型類型不能協變的原因。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章