第41条:慎用重载

下面代码意图是用重载识别参数类型。但由于客户端代码编写的问题造成类输出意外的结果。

public class CollectionClassifier {
    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)); 
        System.out.println( classify(new HashSet<String>()));
        System.out.println( classify(new ArrayList<BigInteger>()));
        System.out.println( classify(new ArrayList<BigInteger>())); 
    }
}
输出结果是:

Unknown Collection
Unknown Collection
Unknown Collection
Set
List
List
前三个输出结果不是我们预期的,这是因为collection中的元素编译阶段的类型都是Collection<?>的。

1、对于重载方法的选择是静态的(编译时类型),而对于覆盖方法的选择是动态的(运行时类型)。

可以将上面代码与下面代码比较:

// Overriding demonstration - Page 192

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());
    }
}
同样的wines中的元素的编译时类型都是Wine类型,但运行却可以调用各自重写的方法。 这说明重写方法的选择是有运行时类型决定的。

1、所以对于重载安全保守的策略是:永远不要导出两个具有相同参数数目的重载方法。如果方法使用可变参数,保守的做法是不要重载他。

2、ObjectOutputStream类的每一种基本类型的write()方法都有变形,但是他采用的不是重载,而是将wite()方法命名为writeBoolean()、writeInt()和writeLong()等。

但是对于构造器,没有这种选择名称的机会。

3、当参数类型相同时,但两个方法参数中至少有一对具有完全不同的类型,那么这样的重载方法的选择就变得容易了。如:ArrayList有带一个int参数构造器,还有一个Collection参数构造器,这两个构造器是不容易混淆的。

一下提供一个很有趣的例子。

public class SetList {
    public static void main(String[] args) {
        Set<Integer> set = new TreeSet<Integer>();
        List<Integer> list = new ArrayList<Integer>();

        for (int i = -3; i < 3; i++) {
            set.add(i);
            list.add(i);
        }

        for (int i = 0; i < 3; i++) {
            set.remove(i);
            list.remove(i);
        }

        System.out.println(set + " " + list);
    }
}
我们期望的输出结果一定是:-3 -2 -1 -3 -2 -1;但是实际的输出结果是:-3 -2 -1 -2 0 2;

解释一下其中的原因set.remove()方法调用的是重载方法remove(E),E指的是任意包装类型。虽然参数类型传入的是intz值,但过程中自动装箱,自然也就移除了set中相应的对象。但是list.remove()方法调用的却是重载方法remove(int),该方法移除集合中下表为参数指定的元素,所以并没有达到用户意愿。

发生上述代码中的问题在某种程度上说也是由于引进了自动装箱机制造成的。(说的概括一点:不允许发生这样的情况-----同一组参数只需经过简单的类型转换就能传递给不同的构造方法)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值