歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Java 協變數組和類型擦除(covariant array & type erasure)

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

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

Copyright © Linux教程網 All Rights Reserved