第8條:覆蓋equals時請遵照通用約定

覆蓋equals方法看似很簡單,可是有許多覆蓋方法或致使錯誤,避免這些錯誤最直接的方法就是不覆蓋equals。
至於何時不覆蓋equals方法,主要有下面三種:
   1:類的每一個實例本質上是惟一的。
      對於代碼活動實體而不是值的類,如Thread,Object提供的equals實現就是這些類的行爲
   2:不關心類是否提供了「邏輯相等」的測試功能
      如Random類提供了隨機數產生的能力,Object繼承過來的equals已經足夠了
   3:超類已經覆蓋了equals,從超類繼承過來的行爲對於子類也是適合的
      如Set實現都是從AbstractSet繼承equals實現,List實現從AbstractList繼承equals實現,Map實現從AbstractMap繼承equals實現。
   4:類是私有的或者包級私有,能夠肯定它的equals方法用於不會被調用。不過爲了以防被意外調用,最好仍是覆蓋下equals
       @Override
       public boolean equals(Object o){
         throw new AssertionError();
       }數組

 

那何時應該覆蓋equals方法呢?
  若是類具備本身特有的「邏輯相等」(不一樣意對象同等),並且超類沒有覆蓋equals來實現指望的行爲,這時候就須要覆蓋equals方法了。一般這種類是「值類」,僅僅表示值的類,如Integer,Date,在利用equals方法時比較對象引用時,但願知道它們在邏輯上是否相等(值是否相等),而不是它們是否指向同一個對象。這不只必須覆蓋equals方法,還可讓這個類的實例能夠被用做映射表map中的key值或set中的元素。一種特殊的「值類」,實例受控確保「每一個值至多隻存在一個對象」的類,如枚舉類型,對應這樣的類,邏輯等同域對象等同是一樣的,所以Object提供的equals以知足,就無需覆蓋。
dom

 

在覆蓋equals時,必須遵照它的通用約定,約定內容以下:
  1:自反性,對於任何非null的引用值x,x.equals(x)必須返回true
  2:對稱性,對於任何非null的引用值x和y,當且僅當y.equals(x)返回true時,x.equals(y)必須返回true。
  3:傳遞性:對於任何非null的引用值x、y和z,若是x.equals(y)返回true,而且y.equals(z)返回true,那麼x.equals(z)也必須返回true。
  4:一致性:對於任何非null的引用值x和y,只要equals的比較操做在對象中所用信息沒有被修改,那麼屢次調用x.equals(y)就會一致地返回true或一致地返回false。
     對於任何非null的引用值x,x.equals(null)必須返回false。ide

 

實現高質量equals方法的訣竅:
  1:用==操做符來檢查「參數是不是這個對象的引用」。若是是,則返回true。這樣作是爲了提升性能。
  2:使用instanceof操做符來檢查「參數是否爲真確類型」。若是不是,則返回false。「正確類型」通常指的是equals方法所在的類。有些狀況是指該類所實現的某個接口。
  3:把參數轉化爲真確的類型。在被instanceof檢測事後,咱們就將Object強行轉換成上面所提到的「正確的類型」,instanceof保證了咱們轉換的真確性。
  4:對於該類中的每一個關鍵域(字段),檢查參數中的域是否與該對象中所對應的域相匹配。強轉以後,就能夠開始匹配兩個對象中的字段了。若是匹配成功就能夠返回true,
     若是「正確的類型」是一個接口,那麼須要接口提供方法來訪問這些字段,若是是個類的話,那就不用說了。字段匹配技巧:
      a、若是是非float和double類型的基本數據類型,那麼直接使用==符號。
      b、若是是float和double,則使用Float.compare和Double.compare方法。
      c、其餘類型(也就是那些須要new出對象的類)則調用他們自身的equals方法。有些字段可能被容許爲空,因此要進行判斷,以下:
        field == null ? o.field == null : filed.equal(0.field);
      d、數組的話須要遍歷每個元素進行匹配,匹配的時候參考上面的三條方法。性能

//一個簡單例子
@Override
public boolean equals(Object obj){
        if(obj == this){//引用是不是指向同一個Person對象
            return true;
        }else if(obj != null&&obj instanceof Person){//類型不爲空,且爲Person類型或子類。
            Person p = (Person) obj;
            if(this.name == null?this.name==p.getName():this.name.equals(p.getName())){
                if(this.age==p.getAge() && this.sex==p.getSex()){
                    return true;
                }
            }
        }
        return false;
}

@Override
public int hashCode(){
  ......
}

覆蓋equals方法的時候咱們還須要注意的地方:
  1:覆蓋equals的時候老是要覆蓋hashCode方法。(爲何覆蓋和怎麼覆蓋會在下一篇文章講)
  2:不要企圖讓equals方法過於智能。只是匹配對象的類型和對象中的各個參數的話很容易作到,而且通常不會違反上面提到的規範。若是你過分的去尋求各類等價關係,那麼上面的約定將很難遵照。
  3:覆蓋equals方法的時候請在方法前面加@Override註解。@Override註解能夠防止本想覆蓋而錯寫成重載的方法,若是你的目的是覆蓋,就使用該註解,這樣在你出錯的時候,能提示你你寫的方法並非一個覆蓋的方法。測試

相關文章
相關標籤/搜索