2015062805 - EffactiveJava笔记 - 第41条 慎用重载(1)

来源:互联网 发布:淘宝店铺的定位 编辑:程序博客网 时间:2024/05/21 12:38

   20150628 星期日 北京

   下面程序的意图是好的,它视图根据一个集合collection是Set,List,还是其他集合类型,进行分类.

   import java.math.BigInteger;

   import java.util.ArrayList;

   import java.util.Collection;

   import java.util.HashMap;

   import java.util.HashSet;

   import java.util.List;

   import java.util.Set;

 

   public class CollectionClassifier {

       publicstatic String clsssfy (Set<?> s) {return "Set";}

       publicstatic String clsssfy (List<?> s) {return "List";}

       publicstatic String clsssfy (Collection<?> s) {return "Unknow Collection";}

      

       publicstatic void main(String[] args) {

              Collection<?>[]collections = {

                            newHashSet<String>(), newArrayList<BigInteger>(),

                            newHashMap<String, String>().values()

              };

             

              for(Collection<?> c : collections) {

                     System.err.println(clsssfy(c));

              }

       }

    }

 

   可能期待的结果是Set,List,Unknow Collection.但是实际结果是打印三次UnknowCollection.

   为什么会这样呢?因为clsssfy方法被重载了,要调用哪个重载方法是在编译时做出决定的.

   [要调用重载的方法是在编译时决定的,要调用重写的方法是在运行时决定的]

   对于for循环中的三次迭代,参数的编译时类型都是Collection<?>.虽然每次迭代运行时类型是不同的,但是这并不影响重载方法的选择,因为该参数编译时类型是Colletion<?>,所以唯一合适的重载方法是clsssfy(Collection<?> s),在循环中每次迭代都会调用它.

 

   是否很困惑呢?

   对于重载方法的选择是静态的,对于重写的方法选择是动态的.选择被重写的方法的正确版本是运行时进行的,选择的依据是被调用方法所在对象的运行时的类型.

   当一个子类包含的方法声明与其父类中的方法声明具有相同的签名时,方法被重写了.如果该方法时子类的实例被调用,那么子类中重写的方法被执行,而不管该子类实例的编译时类型到底是什么.[多态的典型用法]

   [以下内容是自我复习了]

   public class Overrding {

       publicstatic void main(String[] args) {

              Person[]persons = {new Person(), new Man(),new WoMan()};

             

              for(Person person : persons) {

                     System.err.println(person.eat());

              }

//           Personeat

//           Maneat

//           WoManeat

       }

}

class Person {publicString eat(){return "Person eat" ;}}

class Man extends Person {

       @Override

       publicString eat() {return "Man eat";}

}

class WoMan extends Person {

       @Override

       publicString eat() {return "WoMan eat";}

}

   eat方法在类Person中声明,在类WoMan和Man类中重写,输出结果是我们预期的结果.

   在循环的迭代中,实例的编译类型是Person.调用被重写的方法,对象的编译类型不会影响哪个方法被执行.”最为具体的”那个重写方法总是被执行.

   和重载相比,重载情景下的对象运行时类型并不影响”哪个重载版本将被执行”,选择工作是编译时进行的,完全基于参数的编译时类型.

    CollectionClassifier示例中,程序意图是期待编译器根据参数的运行时类型自动调用分发给适当的重载方法,以此来识别参数类型.好比Person中的eat方法.但是方法重载机制没有提供此功能.

   如何修正呢?使用一个方法,重载时使用instanceof判断类型.

       publicstatic String clsssfy (Collection<?> s) {

              if(s instanceof Set) {

                     return"Set";

              }else if (s instanceof List) {

                     return"List";

              }else {

                     return"Unknow Collection";

              }

       }

   [上面是我写的,很清晰,但是不简洁,下面提供的是原书自带的代码,非常简洁,明了,经济,将三目运算符运用到一种举重若轻的地步]

   public static String clsssfy (Collection<?>s) {

         returns instanceof Set ? "Set" : s instanceof List ? "List" :"Unknow Collection";

   }

   重写机制是规范,重载机制是例外.所以,重写机制满足人们对方法调用行为的期望, CollectionClassifier案例中使得这种期望落空.

   对于API来说尤为重要,如果API普通用户根本不知道”对于一组给定的参数,哪个重载方法被调用”,那么发生错误是必然的,并且在运行时发生怪异的错误.所以避免胡乱使用重载机制.

   怎么才算是胡乱使用呢?问题有争议.

   怎么避免胡乱使用呢?保守而安全的策略是,永远不要调用两个具有相同参数个数的重载方法.

   如果方法是可变参数,保守的策略是根本不要重载它.

   你可以给方法使用不同的名称,而不用重载机制.

   案例,考虑ObjectOutputStream类,对于每个基本类型,以及几种引用类型,它的write方法都有变形.变形方法就是不同的名称,而不是重载.诸如writeBoolean(boolean),writeInt(int)这样的签名.与重载方法比较,这种命名模式的好处是,有机会提供相应名称的读方法. ObjectOutputStream类就是这么做的,readBoolean(),readInt()等等.

0 0
原创粉丝点击