下列的程式碼,輸出會是什麼? 或許跟你想的不一樣。它希望透過類型來區分集合是 Set、List 還是 Collection。
public static String classify(Set<?> s) { return "Set"; }
public static String classify(List<?> lst) { return "List"; }
public static String classify(Collection<?> c) { return "Unknown Collection"; }
public static void main(String[] args) {
Collection<?>[] collections = { new HashSet<String>(),
new ArrayList<BigInteger>(),
new HashMap<String, String>().values() };
for (Collection<?> c : collections)
System.out.println(classify(c));
}
正確輸出如下,因為程式重載 (overload) classify 方法,要調用哪一個重載方法是在編譯時就決定的,不是在執行時,根據實體來決定運行方法。Unknown Collection
Unknown Collection
Unknown Collection
對於重載 (overload) 方法的選擇是靜態的,而對於覆蓋 (override) 方法的選擇是動態的,下面使用繼承方式來覆寫方法。class Wine {
String name() { return "wine"; }
}
class SparklingWine extends Wine {
@Override
String name() { return "sparkling wine"; }
}
class Champagne extends SparklingWine {
@Override
String name() { return "champagne"; }
}
public class Overriding {
public static void main(String[] args) {
Wine[] wines = { new Wine(), new SparklingWine(), new Champagne() };
for (Wine wine : wines)
System.out.println(wine.name());
}
}
輸出結果是會根據實體是什麼來運行它的方法。wine
sparkling wine
champagne
所以 classify 方法不需要重載,改成單一方法,並使用 instanceof 來測試。public static String classify(Collection<?> c) {
return c instanceof Set ? "Set" :
c instanceof List ? "List" : "Unknown Collection";
}
覆蓋是標準規範,而重載是例外。對 API 來說,如果一組固定參數,會導致調用者不知道哪個重載方法會被調用,那就有機會發生錯誤,而且往往會在運行時才發現。應避免胡亂地使用重載機制,安全保守的方式為,不要導出兩個具有相同參數數量的重載方法,這樣就不會有疑慮。如果方法的參數數量一樣,就可以考慮使用不同名稱來命名方法,不需要使用重載,像是 readInt() 及 readLong()。在 constructor 是無法命名的,所以當一個 class 有多個 constructor 時,就是重載。另外也可以選擇靜態工廠的方式來取代 constructor。雖然大部分的 class 都遵循上述規則,但也不是沒有例外,像是 String 就有兩個重載的靜態工廠方法:valueOf(char[]) 及 valueOf(Object),可視為一個反例。
轉載請註明原文網址 https://cookieandcoketw.blogspot.com/2021/03/effective-java-52-overload.html
沒有留言:
張貼留言