讀《effective java》學習筆記三

          第六條 在改寫equals的時候請遵守通用規定
            有一種“值類”可以不要求改寫equals方法,類型安全枚舉類型,因為類型安全枚舉類型保證每一個值之多只存在一個對象,所有對于這樣的類而言,Object的queals方法等同于邏輯意義上的equals方法。
           
            在改寫equals方法的時候,要遵循的規范: 
                  1,自反性。對任意的引用值x,x.equals(x)一定是true
                  2,對稱性。對于任意的引用值x和y,當且僅當y.equals(x)返回true時,x.equals(y)也一定返回true.
                  3,傳遞性。對于任意的引用值x,y和z,如果x.equals(y)==true and y.equals(z)==true,so x.equals(z)==true.
                  4,一致性。對于任意的引用值x和y,如果用于equals比較的對象信息沒有被修改的話,那么,多次調用x.equals(y)要么一致地返回true,要么一致地返回false.
                  5,非空性。對于任意的非空引用值x,x.equals(null)一定返回false.
                  
            自反性:要求一個對象必須等于其自身。一個例子:你把該類的實例加入到一個集合中,則該集合的contains方法
          將果斷地告訴你,該集合不包含你剛剛加入的實例. 

            對稱性:
            例如:
               public final class CaseInsensitiveString{
                     private String s;
                     public CaseInsensitiveString(String s){
                        if(s==null)   throw new NullPointerException();
                        this.s=s;
                     }
                     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 but s.equals(cis)==false
          這就違反了對稱性的原則.為了解決這個問題,只需把企圖與String互操作的這段代碼從equals方法中去掉舊可以了.這樣做之后,你可以重構代碼,使他變成一條返回語句:
          public boolean equals(Object o){
             return o instanceof CaseInsensitiveString && ((CaseInsensitiveString)o).s.equalsIgnoreCase(s);
          }

           

          傳遞性---即如果一個對象等于第二個對象,并且第二個對象又等于第三個對象,則第一個對象一定等于第三個對象。
          例如:
                 public class Point{
                     private final int x;
                     private final int y;
                     public Point(int x,int y){
                          this.x=x;
                          this.y=y;
                    }
                    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 Color color;
                public ColorPoint(int x,int y,Color color){
                    super(x,y);
                    this.color=color;
                }
             }
          分析: equals方法會怎么樣呢?如果你完全不提供equals方法,而是直接從Point繼承過來,那么在equals座比較的時候顏色信息會被忽略掉。如果你編寫一個equals方法,只有當實參是另一個有色點,并且具有同樣的位置和顏色的時候,它才返回true:
                 public boolean equals(Object o){
                    if(!(o instanceof ColorPoint)) return false;
                    ColorPoint cp=(ColorPoint)o;
                    return super.equals(o) && cp.color==color;
                 }
          分析:這個方法的問題在于,我們在比較一個普通點和一個有色點,以及反過來的情形的時候,可能會得到不同的結果。前一種比較忽略了顏色信息,而后一種比較總是返回false,因為實參的類型不正確。例如:
               Point p=new Point(1,2);
               ColorPoint cp=new ColorPoint(1,2,Color.RED);
          然后:p.equals(cp)==true but cp.equals(p)==false

          修正:讓ColorPoint.equals在進行“混合比較”的時候忽略顏色信息:
             public boolean equals(Object o){
                 if(!(o instanceof Point)) return false;
                 if(!(o instanceof ColorPoint)) return o.equals(this);
                 ColorPoint cp=(ColorPoint)o;
                 return super.equals(o) && cp.color==color;
             }
          這種方法確實提供了對稱性,但是卻犧牲了傳遞性:
             ColorPoint p1=new ColorPoint(1,2,Color.RED);
             Point p2=new Point(1,2);
             ColorPoint p3=new ColorPoint(1,2,Color.BLUE);
          此時:p1.equals(p2)==true and p2.equals(p3)==true but p1.equals(p3)==false很顯然違反了傳遞性。前兩個比較不考慮顏色信息,而第三個比較考慮了顏色信息。

          結論:要想在擴展一個可實例華的類的同時,既要增加新的特征,同時還要保留equals約定,沒有一個簡單的辦法可以做到這一點。根據"復合優于繼承",這個問題還是有好的解決辦法:我們不讓ColorPoint擴展Point,而是在ColorPoint中加入一個私有的Point域,以及一個公有的視圖方法,此方法返回一個與該有色 點在同一位置上的普通Point對象:
              public class ColorPoint{
                 private Point point;
                 private Color color;
                 public ColorPoint(int x,int y,Color color){
                   point=new Point(x,y);
                   this.color=color;
                 }
                 public Point asPoint(){
                     return point;
                 }
               
                 public boolean equals(Object o){
                     if(!(o instanceof ColorPoint)) return false;
                     ColorPoint cp=(ColorPoint)o;
                     return cp.point.equals.(point) && cp.color.equals(color);
                 }
              }
             
          注意,你可以在一個抽象類的子類中增加新的特征,而不會違反equals約定。

           

          一致性:
           如果兩個對象相等的話,那么它們必須始終保持相等,除非有一個對象被修改了。由于可變對象在不同的時候可以與不同的對象相等,而非可變對象不會這樣,這個約定沒有嚴格界定。
           


          非空性:沒什么好說的。

          1,使用==操作符檢查“實參是否為指向對象的一個應用”。如果是的話,則返回true。
                 2,使用instanceof操作符檢查“實參是否為正確的類型”。如果不是的話,則返回false。
                 3,把實參轉換到正確的類型。
                 4,對于該類中每一個“關鍵(significant)”域,檢查實參中的域與當前對象中對應的域值是否匹配
               if (!(this.field == null ? o.field == null : this.equals(o.field)))
               //或者寫成 if(!(this.field == o.field || (this.field != null && this.field.equals(o.field)))) 對于this.field和o.field通常是相同的對象引用,會更快一些。
                 return false;
               //比較下一個field
               //都比較完了
               return true;
               
          5.最后還要確認以下事情
             5.1)改寫equals的同時,總是(必須)要改寫hashCode方法(見【第8條】),這是極容易被忽略的,有極為重要的
             5.2)不要企圖讓equals方法過于聰明
             5.3)不要使用不可靠的資源。如依賴網絡連接
             5.4)不要將equals聲明中的Object對象替換為其他類型。
                   public boolean equals(MyClass) 這樣的聲明并不鮮見,往外使程序員數小時搞不清楚為什么不能正常工作
                   原因是這里是重載(overload)而并不是改寫(override)(或稱為覆蓋、重寫)
                   相當于給equals又增加了一個實參類型為MyClass的重載,而實參為Object的那個equals并沒有被改寫,依然還是從Object繼承來的最初的那個equals,所總是看不到程序員想要的效果。因為類庫或其他人寫的代碼都是調用實參為Object型的那個equals方法的(別人又如何在事前知曉你今天寫的MyClass呢?)

          posted on 2009-07-28 20:30 胡鵬 閱讀(227) 評論(0)  編輯  收藏 所屬分類: 讀《effective java》筆記

          導航

          <2009年7月>
          2829301234
          567891011
          12131415161718
          19202122232425
          2627282930311
          2345678

          統計

          常用鏈接

          留言簿(3)

          隨筆分類

          隨筆檔案

          agile

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 洛阳市| 新丰县| 如皋市| 淄博市| 富裕县| 格尔木市| 霞浦县| 密山市| 原平市| 英山县| 项城市| 鹤岗市| 文化| 明水县| 安新县| 彭阳县| 那坡县| 遵化市| 封开县| 常山县| 文山县| 长宁区| 仁怀市| 巩留县| 中山市| 马尔康县| 忻州市| 玉环县| 德化县| 宜良县| 呈贡县| 吴堡县| 巴青县| 西贡区| 临泽县| 徐水县| 叶城县| 龙陵县| 河池市| 宁海县| 绥滨县|