[英]Understanding equals method
J. Bloch在他的有效Java中為equals方法的實現提供了幾條規則。 他們來了:
•Reflexive:對於任何非空引用值x,x.equals(x)必須返回true。
•對稱:對於任何非空引用值x和y,當且僅當y.equals(x)返回true時,x.equals(y)必須返回true。
•Transitive:對於任何非空引用值x,y,z,如果x.equals(y)返回true而y.equals(z)返回true,則x.equals(z)必須返回true。
•一致:對於任何非空引用值x和y,x.equals(y)的多次調用始終返回true或始終返回false,前提是不修改在對象上的equals比較中使用的信息。
•對於任何非空引用值x,x.equals(null)必須返回false。
但后來他在書中提到了所謂的利斯科夫替代原則:
Liskov替換原則表明類型的任何重要屬性也應該適用於其子類型,因此為該類型編寫的任何方法都應該在其子類型上同樣有效
我不知道它與equals
合同的關系。 我們是否應該在編寫equals實現時堅持它?
問題是關於實現子類的方法。 以下是本書中的示例:
private static final Set<Point> unitCircle;
static {
unitCircle = new HashSet<Point>();
unitCircle.add(new Point(1, 0));
unitCircle.add(new Point(0, 1));
unitCircle.add(new Point(-1, 0));
unitCircle.add(new Point(0, -1));
}
public static boolean onUnitCircle(Point p) {
return unitCircle.contains(p);
}
public class CounterPoint extends Point {
private static final AtomicInteger counter = new AtomicInteger();
public CounterPoint(int x, int y) {
super(x, y);
counter.incrementAndGet();
}
public int numberCreated() { return counter.get(); }
}
以及以下實施:
// Broken - violates Liskov substitution principle (page 40)
@Override public boolean equals(Object o) {
if (o == null || o.getClass() != getClass())
return false;
Point p = (Point) o;
return p.x == x && p.y == y;
}
好的,違反了什么呢? 我不明白。
通常有兩種方法可以檢查equals方法中的類型:
選項1:instanceof
if (! (obj instanceof ThisClass)){
return false;
}
該選項尊重 Liskov替代原則 。 但是你不能在子類這是相關的而不會斷裂的等價關系(自反,對稱,傳遞)的特性equals方法添加附加屬性。
選項2:getClass()
if (obj == null || ! this.getClass().equals(obj.getClass())) {
return false;
}
該選項違反了Liskov替換原則 。 但是,您可以在子類中添加與equals方法相關的其他屬性,而不會破壞等價關系的特征(自反,對稱,傳遞)。
Joshua Bloch在他的“Effective Java”一書中警告過這一點。
然而,Angelika Langer提到了一種“混合tpye”比較的方法,如果你可以為其他屬性定義默認值:
http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html
缺點是等於方法變得相當復雜。
// Broken - violates Liskov substitution principle (page 40) @Override public boolean equals(Object o) { if (o == null || o.getClass() != getClass()) return false; Point p = (Point) o; return px == x && py == y; }
好的,違反了什么呢? 我不明白。
因此,如果您有一個子類,如MyPoint(可能會添加其他方法但不添加其他屬性/字段),那么
Point p1 = new Point(x, y);
Point p2 = new MyPoint(x, y);
p1.equals(p2) == false
Set<Point> points = new HashSet<>();
points.add(p1);
points.contains(p2) == false;
雖然兩個對象確實代表了同一點。
如果您改為使用選項1(instanceof),則equals方法將返回true。
我認為他試圖說一個點的特征是它的坐標。 所以你會期望這是真的:
new Point(0, 0).equals(new CounterPoint(0, 0));
因為這兩個點具有相同的坐標,即使它們沒有相同的類型。 但是建議的equals方法將返回false,因為這兩個對象具有不同的類。
如果您考慮集合,例如,這是真的:
new LinkedList().equals(new ArrayList());
這兩個列表的類型不同,但它們具有相同的內容(在這種情況下它們都是空的),因此被認為是相同的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.