在講下一個新的功能之前,我們先來復習一下什麼是泛型(Generic)。
在 Java SE 1.5 版之前,還沒有納入泛型的語法,沒有泛型有什麼壞處呢?我們拿最常用的 Collection 類別來看看:
package idv.jacky.ironman4.day16;
import java.util.ArrayList;
import java.util.List;
public class Day16Example1 {
public static void main(String[] args) {
List fruits = new ArrayList();
fruits.add(new Apple());
fruits.add(new Banana());
fruits.add(new Apple());
makeJuice(fruits);
}
public static void makeJuice(List fruits) {
for(Object obj : fruits) {
Apple a = (Apple)obj;
a.makeJuice();
}
}
}
上面的程式碼裡,我們宣告了一個 fruits 的 List 物件(用ArrayList 為實體),然後依序放入了 Apple,Banana 和 Apple 這三個物件。Apple 和 Banana 的程式碼如下:
package idv.jacky.ironman4.day16;
public class Apple {
public void makeJuice() {
}
}
package idv.jacky.ironman4.day16;
public class Banana {
}
Apple 跟 Banana 兩個類別最大的不同在於,Apple 類別裡有個 makeJuice 的方法,而 Banana 類別裡沒有(應該沒有人打香蕉汁來喝吧??)。回到最上面的程式碼,有個叫 makeJuice 的方法,所傳入的參數是個 List 物件;在方法裡我們會把 List 物件裡的物件一個個拿出來,轉成 Apple 物件後呼叫物件自己的 makeJuice 方法。
整個程式可以正常的編譯,但在執行時就出了問題了,錯誤訊息是
Exception in thread "main" java.lang.ClassCastException: idv.jacky.ironman4.day16.Banana cannot be cast to idv.jacky.ironman4.day16.Apple
at idv.jacky.ironman4.day16.Day16Example1.makeJuice(Day16Example1.java:19)
at idv.jacky.ironman4.day16.Day16Example1.main(Day16Example1.java:14)
是的,當程式試著將一個 Banana 物件 強迫轉型成 Apple 物件時,JVM 就會丟出 ClassCaseExcetpion!因為香蕉不可能變成蘋果!
Java 是一種 強型別程式語言(Stong Typing Programming Language),簡單的說就是,Java 程式碼在編譯時期,就會檢查各個宣告的變數,是否已指定了正確的型態。不過像這個例子,Java 編譯器確束手無策,我們明明知道,程式第19行有潛在的危險,但確還是讓程式碼能成功編譯。一直到執行時期,才丟出例外,但為時已晚。
基於這類的原因,Java SE 1.5 版終於在大家的期盼下加入了泛型這個功能!於是我們改造了一下這個範例
package idv.jacky.ironman4.day16;
import java.util.ArrayList;
import java.util.List;
public class Day16Example2 {
public static void main(String[] args) {
List<Apple> fruits = new ArrayList<Apple>();
fruits.add(new Apple());
fruits.add(new Banana());
fruits.add(new Apple());
makeJuice(fruits);
}
public static void makeJuice(List<Apple> fruits) {
for(Apple a : fruits) {
a.makeJuice();
}
}
}
泛型的使用方式很簡單,就是在 Collection 類別後加上角刮號(大於、小於),然後把型別指定進去。以程式第9行來說,就是宣告一個只能放 Apple 物件的 List 物件。既然宣告只能放 Apple 物件,那原本的第11行在這裡就會出錯,程式會沒辦法成功地編譯,編譯時會告訴你錯誤和原因:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The method add(Apple) in the type List<Apple> is not applicable for the arguments (Banana)
at idv.jacky.ironman4.day16.Day16Example2.main(Day16Example2.java:11)
這樣一來,我們在寫程式的過程式,就會修正這個錯誤,而剛剛執行時期的錯誤也就不會發生,讓程式能更穩定的執行!
這是泛型的第一種基本應用 - 限制,限制你不能將不同的型別,指定給某種類別,例如 Collection。