2021/03/23

[筆記] Effective Java #52 慎用重載

Effective Java 3rd 簡體中文版筆記 #52 慎用重載
下列的程式碼,輸出會是什麼? 或許跟你想的不一樣。它希望透過類型來區分集合是 SetList 還是 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

沒有留言:

張貼留言