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