【Effective Java】條41:慎用重載

重載(Overload)和重寫(Override

重載是指,同一個類裏,方法名相同,方法入參類型不同或者個數不同的方法;重寫是指子類重寫父類的某個方法。不僅僅是概念上的不同,在運行時調用哪種方法的選擇上也不同。如首先看個重載的示例:

public class CollectionClassfier {

  public static String classify(Set<?> s) {
    return "set";
  }

  public static String classify(List<?> s) {
    return "list";
  }

  public static String classify(Collection<?> s) {
    return "unknown";
  }

  public static void main(String[] args) {
    Collection<?>[] collections = {
        new HashSet<String>(),
        new ArrayList<String>(),
        new HashMap<String, String>().values()
    };

    for (Collection<?> collection : collections) {
      System.out.println(classify(collection));
    }
  }

}

以上代碼輸出的是:

unknown
unknown
unknown

和我們預期的不同。

set
list
unknown

再比如重寫的例子:

public class Wine {

  String name() {
    return "Wine";
  }

  static class SparkingWine extends Wine {

    @Override
    String name() {
      return "SparkingWine";
    }
  }

  static class Champagne extends SparkingWine {

    @Override
    String name() {
      return "Champagne";
    }
  }

  public static void main(String[] args) {
    Wine[] wines = {
        new Wine(),
        new SparkingWine(),
        new Champagne()
    };

    for (Wine wine : wines) {
      System.out.println(wine.name());
    }
  }

}

輸出的結果符合我們的預期。

Wine
SparkingWine
Champagne

那麼爲什麼重載和重寫會不同呢?那是因爲重載是根據編譯期類型判斷調用的方法,“重載”示例中,collections編譯期都是Collection類型,那麼其調用的方法都是public static String classify(Collection<?> s)此方法。而重寫是根據運行時類型來判斷調用哪個方法的,即wines中第一個元素調用的是Wine中的name()方法,其他元素依次。

重載的困惑

正如上面的示例,重載會容易使調用者困惑,不能明確知道程序會如何運行,這是很危險的。那麼該如何防止這樣的困惑?
1. 在類中,絕不要使重載的兩個方法存在相同數目的入參;
2. 如果方法入參數目不可避免相同,則需使入參類型不同,這樣也可以區分;
3. 如果方法入參數目相同,也可修改方法的名稱,使其可以區分。如ObjectOutputStream中的writeBoolean(boolean)writeInt(int)writeLong(long)
4. 如果方法入參是可變參數的,則儘量不要重載。

可變參數重載的例外情形:
由於可變參數有性能上的問題(需要將入參轉爲數組,爲數組分配內存並初始化),但可變參數的優點就是靈活。如果需要考慮到兩個之間的平衡,可以借鑑EnumSet中的of方法。

static <E extends Enum<E>> EnumSet<E>   of(E e)
static <E extends Enum<E>> EnumSet<E>   of(E first, E... rest)
static <E extends Enum<E>> EnumSet<E>   of(E e1, E e2)
static <E extends Enum<E>> EnumSet<E>   of(E e1, E e2, E e3)
static <E extends Enum<E>> EnumSet<E>   of(E e1, E e2, E e3, E e4)
static <E extends Enum<E>> EnumSet<E>   of(E e1, E e2, E e3, E e4, E e5)

假設某個方法有95%的概率是調用3個參數或者更少,則可以作如下聲明:

public void foo(){}
public void foo(int a1){}
public void foo(int a1, int a2){}
public void foo(int a1, int a2, int a3){}
public void foo(int a1, int a2, int a3, int... rest){}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章