effective java摘抄(一)

来源:互联网 发布:淘宝费列罗真假 编辑:程序博客网 时间:2024/06/05 08:20

一、覆盖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对象替换为其他的类型。

0 0
原创粉丝点击