Java筆記(一) 協變性、陣列與泛型
前言
在開始前,我們先看一段有點“誤導性”的程式碼,下面的程式碼在編譯時不會產生任何異常。
package test; public class Test { private interface Shape{ } private class Square implements Shape{ } private class Circle implements Shape{ } public static void main(String[] args) { Shape[] arr=new Square[5]; arr[0]=new Test().new Circle(); } }
但是如果你執行,則會發生 :
Exception in thread "main" java.lang.ArrayStoreException: test.Test$Circle
at test.Test.main(Test.java:17)
協變性
發生上面情形的原因,便是Java陣列中的bug,編譯器在編譯時,並不能預知執行時的情況,所以它能判斷的,只有一個,也即“套皮”是否是對的,也就是,Shape這個皮能否套下Square這個Object,如果它能,編譯也就通過了。在非陣列的情況下,它也就通過了,否則我們也無法用介面來承載類了。
但如果是陣列,那情況就變得複雜了起來,Java中的陣列,具有協變性(
public class Test { private interface Shape{ } private class Square implements Shape{ } private class Circle implements Shape{ } public static void main(String[] args) { ArrayList arraylist=new ArrayList(); arraylist.add(new Test().new Square()); arraylist.add(new Test().new Circle()); Circle circle=(Circle)arraylist.get(0);//報錯 } }
為了解決這個Bug,讓問題得以在編譯器階段就被發現,Java在Java5後,引入了泛型的概念。
泛型
(一)泛型引入後的區別
引入了泛型以後,由於泛型不再具有協變性,ArrayList<Square>套皮ArrayList<Shape>將失敗,上文提到的問題在編譯階段就會被發現。
(二)以前的泛型實現
其實在泛型引入前,泛型也是有其實現方式的,其一便是Object作為引數傳入的方式,所有的類都繼承自Object,所以我們可以這樣操作。其二是用介面實現泛型。
在那之前,先引入一個東西(其實可以單開寫個幾萬字的),物件是封裝了操作的一組資料,其封裝方式不同,所以當以錯誤的方式開啟時,就會出錯。我們傳遞一個類時,無論它是Square還是Circle,只要開啟方式(強轉回去的時候)正確,就不會有事。以泛型方法舉例:
class Prototype{
public void echoInfo(){
System.out.println("this is prototype");
}
}
class Experimental extends Prototype{
}
public class Test {
public static void main(String[] args) {
printItem(new Experimental ());
}
public static void printItem(Object a){
Prototype EC=(Prototype)a;
ec.echoInfo();
}
}
但如果這樣則會產生兩個問題:
- 必須知道要強轉成什麼,而且無法控制傳入非Prototype的子類
- 不轉的話,除了寥寥幾個toString等方法你調不了別的
但引入了泛型以後,可以控制傳入:
class Prototype{
public void echoInfo(){
System.out.println("this is prototype");
}
}
class Experimental extends Prototype{
}
public class Test {
public static void main(String[] args) {
printItem(new Experimental());
}
public static <AnyType extends Prototype> void printItem(AnyType a){
a.echoInfo();
}
}
(三)冗長的傳入
但是引入泛型也非全然沒有缺點,JAVA中泛型不具有協變性,避免了陣列的bug,但是為了使得泛型更通用,泛型提出了萬用字元的概念:<? extends/supers Superclass>
如果一個類或者方法想傳入下面的ArrayList<Square>和ArrayList<Circle>,你需要這句話:
<AnyType extends Comparable<? super AnyType>>
參考資料:
資料結構與演算法分析(Java語言描述) Page 8