標籤:
Java中的所有類都繼承自Object類,Object類中有許多通用的方法,這一章要討論的是:對於Object類中的通用方法,我們的類要不要繼承,以及繼承時需要注意的事項。
第1條:equals(),覆蓋時請遵守通用約定
首先看一下不需要覆蓋的情況:
1.類的每個執行個體本質上是唯一的。(比如Static的,單例的等等),這樣不需要特意覆蓋equals方法,用Object類的equals()方法就足夠了
2.不關心類是否實現了“邏輯相等”的測試功能。我們用equals的目的就是判斷兩個對象是否是“邏輯相等”的,比如String類的值是否相等。如果這個類並不需要這個功能,那麼我們自然沒必要覆蓋equals()方法。
3.超類已經覆蓋了equals()方法,且從超類繼承過來的行為對子類也是合適的。
4.有一種“值類”不需要覆蓋,即執行個體受控類,確保“每個值之多隻存在一個對象”,枚舉類型就是這種類,對於這樣的類,邏輯相等與對象相等是一回事,所以不需要覆蓋equals()方法
那麼什麼時候需要覆蓋呢?
如果類有自己特有的“邏輯相等”的概念,而且父類還沒有覆蓋equals以實現期望的行為,這時我們就需要覆蓋equals()方法了。這通常屬於“值類”。值類通常是一個只表示值的類,如Integer,程式員在用equals比較時僅僅希望知道它們的值是否相等,而關心它們是否指向同一個對象。
equals方法需要實現“等價關係”,包含四個方面:
1.自反性。x.equals(x)必須始終返回true.
2.對稱性。如果x.equals(y)=true,那麼y.equals(x)也必須為true.
3.傳遞性。如果有x.equals(y)= true,y.equals(z) = true,那麼也必有x.quals(z) = true.
4.一致性。只要值沒被修改,那麼多次調用x.equals(y)的值應該始終相等。
下面舉一些違反了這4條的例子。
違反自反性:這種一般很少,因為這一條僅僅要求對象等於自身。如果違反了的話,假設你把一個對象加到一個集合裡,然後查詢,該集合的contains方法會告訴你集合中不存在這個元素。
違反對稱性:考慮下面的類,它實現了一個區分大小寫字串,但比較時不考慮大小寫。
public final class CaseInsensitiveString{ private final String s; ... public boolean equals(Object o){ if( o instanceof CaseInsentiveString){ return s.equalsIgnoreCase( ((CaseInsentiveString) o).s); if( o instanceof String) //考慮了與普通String類的比較 return s.equalsIgnoreCase((String) o); return false; }}
看上去考慮的很好,比較時,考慮了傳入的對象是本類和String類的情況。但是,假設我們有兩個對象:
CaseInsensitiveString cis = new CaseIntensitiveString("Polish");
String s = "polish";
那麼,顯然cis.equals(s)=true。但是問題來了,s.equals(cis) = false。這就違反了對稱性。
解決方案是,如果A類的equals方法中引入了對B類對象的比較,那麼B類反過來也一定要引入對A類的比較。
違反傳遞性:違反這條一般是因為繼承時子類添加了新的值對象。子類添加的資訊會影響equals的比較結果。
比如我們有一個簡單的Point類:
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 this.x == p.x && this.y == p.y; }}
假設你想擴充這個類,為每個點加一個顏色資訊:
public class ColorPoint extends Point{ private final Color color; public ColorPoint(int x,int y ,Color c){ super(x,y); color = c; }}
equals方法是怎樣呢?有兩種考慮:1.只和有色點比較,要x,y,color都相等才返回true,對於不是有色點的對象,返回結果始終是false
2.有色點也能夠和普通點比較,如果和普通點比較,就比較x,y的值。
看第一種方法的實現:
public boolean equals(Object o){ if(!(o instance of ColorPoint)){ return false; return super.equalso) && ((ColorPoint) o).color == color; }}
如果有一個有色點對象cp(1,2,red)和一個普通點對象p(1,2),那麼p.equals(cp) = true,cp.equals(p) = false。違反了對稱性。
第二種方法:
public boolean equals(Object o){ if(!(o instanceof Point)) return false; if(!(o instanceof ColorPoint))//如果對象不是colorPoint類的話,就用這個對象的equals方法來判斷 return o.equals(this); return super.equalso) && ((ColorPoint) o ).color = color;}
這種方法實現了對稱性,但是犧牲了傳遞性。考慮有色點cp1(1,2,red),cp2(1,2,blue)和普通點p(1,2)
有cp1.equals(p) = true ,p.equals(cp2) ,但是cp1.equals(cp2)。違反了傳遞性。
我們無法在擴充可執行個體化的類的同時,既增加新的值組件,又保留equals約定。
怎麼解決呢?
複合優先於繼承。
我們不再繼承Point類,而是建立一個ColorPoint類,在其中加入一個Point類的引用。
今天先寫到這,明天繼續
effective java讀書筆記——對於所有對象都通用的方法