重載(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){}