第41条:慎用重载

试图根据一个集合是Set、Liist、还是其他的集合类型,对它进行分类的程序:

public class CollectionClassifier {
    public static String classify(Set<?> s) {
        return "Set";
    }
    public static String classify(List<?> l) {
        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));
        }
    }
}

期望打印出的是Set,List,Unknown Collection,但实际上却是三个Unknown Collection。classify方法被重载,而要调用哪个重载方法是在编译时做出决定的,而三个集合的参数在编译时的类型都是Collection<?>,虽然运行时的类型不同,但不影响对重载方法的选择。所以唯一适合的方法是参数为Collection<?>的那一个。

与覆盖方法不同,覆盖方法的选择是动态的,选择的依据是运行时的类型,而不管编译时的类型是什么。

 

修正方案,使用单个方法替换重载的classify方法,也就是不使用重载机制:

public static String classify(Collection<?> c) {
    return c instanceof Set ? "Set" :
            c instanceof List ? "List" : Unknown Collection";
}

 

ObjectOutputStream类,对于每个基本类型,以及几种引用类型,它的write方法的命名都不一样,如writeInt,writeBoolean,writeLong等。这是一种好的实践。

 

Java 1.5发行后,出现了自动装箱,引起的一些问题:

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);
    }

}

打印的结果是:

原因是List有remove(int)和remove(Object)方法,Set只有一个remove(Object)方法,list.remove(i)调用的是remove(int)方法,所以导致这样的结果。

修改成list.remove((Integer) i);就能使结果一致了。

 

对于多个具有相同参数数目的方法来说,应该尽量避免重载方法,对于构造器,可以选择静态工厂,避免参数只需经过类型转换就可以被传递给不同的重载方法,如果不能避免,应保证传递相同参数,所有的重载方法行为一致,否则对程序员有效使用重载方法会造成困难。

 

posted @ 2016-08-06 23:56  没有梦想的小灰灰  阅读(266)  评论(0编辑  收藏  举报