【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){}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值