effective java摘抄(一)

一、覆蓋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對象替換爲其他的類型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章