您的位置:首页 > Web前端

effective java摘抄(一)

2017-04-12 14:09 330 查看
一、覆盖equals请遵守通用约定

   1、覆盖equals的约定

         自反性:对于任何非null的引用值x,x.equals(x)必须返回true。

         对称性:对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。

         传递性:对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)返回true,那么x.equals(z)也必须返回true。

         一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致地返回true,或者一致的返回false。

         对于任何非null的引用值x,x.equals(null)必须返回false。

   2、违反对称性的案例:

         public class CaseInsensitiveString {

              private final String s;

    

              public CaseInsensitiveString(String s){

                   if(s==null)

                         throw new NullPointerException();

                   this.s = s;

              }

             @Override

             public boolean equals(Object o) {

                  if(o instanceof CaseInsensitiveString){

                       return s.equalsIgnoreCase(((CaseInsensitiveString) o).s);

                  }

                  if(o instanceof String)

                      return s.equalsIgnoreCase((String)o);

                 return false;

            }

      }

      对于以上代码:如果假设现在有两个字符串,一个不区分大小写的,另一个是普通字符串

           CaseInsensitiveString cis =  new CaseInsensitiveString("Polish");

           String s = "polish";

           cis.equals(s);    //返回true

           s.equals(cis);    //返回false

       虽然CaseInsensitiveString中的equals知道普通的字符串对象,但是String类中的equals方法并不知道不区分大小写的字符串,因此s.equals(cis)返回false。这就违反了对称性

      3、违反对称性或传递性的案例:

          public class Point {

               private final int x;

               private final int y;

               public Point(int x,int y){

                     this.x = x;

                     this.y = y;

               }

    

              @Override

              public boolean equals(Object o) {

                    if(!(o instanceof Point)) return false;

                    Point p = (Point)o;

                    return p.x == x && p.y == y;

              }

         }

        现在扩展当前类,为点增加颜色属性

       public class ColorPoint extends Point{

            private final Color color;

            public ColorPoint(int x,int y,Color color){

                 super(x,y);

                 this.color = color;

            }

            @Override

            public boolean equals(Object o) {

                 if(!(o instanceof ColorPoint)) return false;

                 return super.equals(o) && ((ColorPoint)o).color == color;

            }

      }

      这个问题在于,比较普通点和有色点,以及有色点和普通点反过来对比时可能会得到不同的结果。普通点和有色点比较时忽略了颜色信息,但有色点和普通点比较时永远返回false因为参数的类型不正确。这违反了对称性,改进一下ColorPoint的equals方法让它符合对称性,但会牺牲传递性;

             @Override  

             public boolean equals(Object o) {

                  if(!(o instanceof Point)) return false;

                  if(!(o instanceof ColorPoint)) return o.equals(this);

                  return super.equals(o) && ((ColorPoint)o).color == color;

             }

            这方法解决了对称性问题但牺牲了传递性

           ColorPoint p1 = new ColorPoint(1, 2,Color.RED);

           Point p2 = new Point(1,2);

           ColorPoint p3 = new ColorPoint(1, 2,Color.BLUE);

        

           System.out.println(p1.equals(p2));   //返回true

           System.out.println(p2.equals(p3));   //返回true

           System.out.println(p1.equals(p3));   //返回false

           因为前两个比较中忽略了颜色信息,第三个比较中比较了颜色,并且颜色不同,所以返回false;

      注意:我们无法在扩展可实例化的类的同事,既增加新的值组件,同时又保留equals约定,除非愿意放弃面向对象的抽象所带来的的优势。但是我们可以在一个抽象类的子类中增加新的值组件,而不会违反equals约定。

          防止违反一致性,无论类是否可变,都不要使equals方法依赖于不可靠的资源。

   4、实现高质量equals的方法的诀窍:

        a、使用==操作符检查“参数是否为这个对象的引用”。

        b、使用instanceof操作符检查“参数是否为正确的类型”。

        c、把参数转换为正确的类型。

        d、对于该类中的每个“关键(significant)”域,检查参数中的域是否与该对象中对应的域相匹配。如果这些测试全部返回成功,则返回true,否则返回false。

注意:1、覆盖equals方法时总要覆盖hashcode方法;2、不要企图让equals方法过于智能;3、不要将equals声明中的Object对象替换为其他的类型。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: