国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

08-重寫 equals 時請遵守通用約定

2019-11-08 03:13:02
字體:
來源:轉載
供稿:網友

重寫 equals 方法有許多的重寫方式會導致錯誤,所以要么不重寫 equals 方法,要么重寫時就要盡力遵守通用約定。

可以不重寫equals方法的情況

如果不重寫equals方法,那該類的每個實例都只與它自身相等,而有時候這就是我們需要的。

1、類的每個實例本質上都是唯一的 對于代表活動實體(例如 Thread),而不是值(Value)的類來說確實如此,Object提供的equals實現對于這些類來說是正確的行為。

2、不關心類是否提供“邏輯相等”的測試功能 有些類我們只關注它提供的功能,而不是實例之間是否相等。比如java.util.Random類,客戶一般用它生成隨機數,基本不會檢測兩個Random生成的隨機數是否相同,所以對于這些類重寫equals方法并沒有意義。

3、父類已經重寫equals,從父類繼承過來的行為對于子類也是合適的 這些在集合框架中比較常見,比如大多數的Set實現都從AbstractSet繼承equals實現,List實現從AbstractList繼承equals實現,Map實現從AbstractMap繼承equals實現。

4、類是私有的或是包級私有的,可以確定它的equals方法永遠不會被調用 這種情況下最好重寫equals方法,以防它被意外調用,可以在重寫equals方法中拋出異常。

@Overridepublic boolean equals(Object obj) { throw new AssertionError();}

重寫equals方法的情景

如果類具有自己特有的“邏輯相等”概念(不同于對象等同概念),而且父類還沒有重寫equals以實現期望的行為,這時我們就需要重寫equals方法,這通常屬于”值類”的情形。

值類僅僅是一個表示值的類,例如Integer和Date,程序猿在利用equals方法來比較值對象的引用時,希望知道它們在邏輯上是否相等。而不是想了解它們是否指向同一個對象。


重寫equals方法的通用約定

1、自反性(reflexive):對于任何非null的引用值x,x.equals(x)必須返回true。

2、對稱性(symmetric):對于任何非null的引用值x和y,當且僅當y.equals(x)返回true時,x.equals(y)必須返回true。

3、傳遞性(transitive):對于任何非null的引用值x,y,z,如果x.equals(y)返回true,并且y.equals(z)返回true,那么x.equals(z)也必須返回true。

4、一致性(consistent):對于任何非null的引用值x和y,只要equals的比較操作在對象中所用的信息沒有被修改,多次調用x.equals(y)就會一致地返回true,或者一致地返回false。

5、對于任何非null的引用值x,x.equals(null)必須返回false。


違反對稱性

舉例:

public final 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; }}

測試:

public static void main(String[] args) { CaseInsensitiveString cis=new CaseInsensitiveString("Java"); String str="java"; System.out.println(cis.equals(str)); System.out.println(str.equals(cis)); }

輸出:

truefalse

當調用 cis.equals(str) 時,使用的是CaseInsensitiveString類的equals方法返回true;但是str.equals(cis)調用的是String類的equals方法返回false;String類是不知道cis是什么鬼,更不知道如何和cis進行比較,肯定返回false ,這就不滿足對稱性了。

解決這個問題的方法,把企圖和String互操作的代碼從equals中刪掉就可以了。

@Overridepublic boolean equals(Object o) { return o instanceof CaseInsensitiveString &&((CaseInsensitiveString)o).s.equalsIgnoreCase(s);}

違反傳遞性

定義二維整數型Point類:

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; }}

之后由于需求,添加顏色信息,對Point類進行擴展:

public class ColorPoint extends Point { private final Color color; public ColorPoint(int x, int y, Color color) { super(x, y); this.color = color; }}

此時如果對ColorPoint調用equals方法,由于其沒有重寫equals方法,因此直接調用從父類繼承過來的equals方法,在比較過程中將忽略顏色信息 ,這樣做雖然沒有違反equals約定,但是不符合“邏輯相等”的期望,因此為ColorPoint提供equals方法:

@Overridepublic boolean equals(Object o) { if (!(o instanceof ColorPoint)) { return false; } return super.equals(o) && ((ColorPoint) o).color == color;}

測試:

public static void main(String[] args) { Point p = new Point(1, 2); ColorPoint cp = new ColorPoint(1, 2, Color.RED); System.out.println(p.equals(cp)); System.out.println(cp.equals(p));}

當p.equals(cp)時,使用的是Point中的equals方法,沒有顏色信息因此返回true;當cp.equals(p)時,使用ColorPoint的equals方法,返回false,不滿足對稱性,可以通過重構equals的方法修復這個問題:

@Overridepublic boolean equals(Object o) { if (!(o instanceof Point)) { return false; } // 不帶顏色的Point,使用Point的equals方法比較 if (!(o instanceof ColorPoint)) { return o.equals(this); } return super.equals(o) && ((ColorPoint) o).color == color;}

上述方法終于保證了對稱性,我們可以使用以下測試數據來進行測試:

public static void main(String[] args) { ColorPoint p1 = new ColorPoint(1, 2, Color.BLUE); Point p2 = new Point(1, 2); ColorPoint p3 = new ColorPoint(1, 2, Color.RED); System.out.println(p1.equals(p2)); System.out.println(p2.equals(p3)); System.out.println(p1.equals(p3));}

當p1.equals(p2)時返回true;p2.equals(p3)時返回true;p1.equals(p3)時返回false,不滿足傳遞性。

這個問題是面向對象語言中關于等價關系的一個基本問題: 無法在擴展可實例化的類的同時,既增加新的值組件,同時又保留equals的約定。

解決這個問題的方法: 面向對象編程中,組合優先于繼承 ,現在的ColorPoint類不再繼承Point類,而是通過一個引用組合它,重構之后的代碼如下:

public class ColorPoint { private final Point point; private final Color color; public ColorPoint(int x, int y, Color color) { if (color == null) { throw new NullPointerException(); } this.point = new Point(x, y); this.color = color; } @Override public boolean equals(Object o) { if (!(o instanceof ColorPoint)) { return false; } ColorPoint cp = (ColorPoint) o; return cp.point.equals(point) && cp.color.equals(color); }}

java 類庫中 java.sql.Timestamp 的 java.util.Date 就違反對稱性約定,可以查看文檔中的免責聲明,這種做法不建議仿效。


非空性測試

很多類使用一個顯式的null測試來避免拋出空指針異常:

@Overridepublic boolean equals(Object o) { if (o == null) { return false; } //... return false;}

其實在equals方法中,最終是將待比較對象轉換為當前類的實例,以調用方法或訪問屬性, 這樣必須先經過 instanceof ,而如果 instanceof 的第一個參數為null,則不管第二個參數是那種類型都會返回false,這樣可以很好地避免空指針異常并且不需要單獨地進行null測試。

@Overridepublic boolean equals(Object o) { if (!(o instanceof MyType)) { return false; } MyType mt = (MyType) o;}

實現高質量equals方法的訣竅

1、使用==操作符檢查 參數是否為這個對象的引用。 2、使用 instanceof 操作符檢查 參數是否為正確的類型。 3、把參數轉換成正確的類型。 4、對于要比較類中的每個關鍵域,檢查參數中的域是否與該對象中對應的域相匹配。 5、編寫完equals方法后需要測試是否滿足對稱性、傳遞性和一致性。


本條目最后的告誡

1、重寫equals時總要重寫hashCode 2、不要企圖讓equals方法過于智能 3、不要將equals聲明中的Object對象替換為其他的類型,因為替換后只是重載Object.equals(Object o),而不是重寫。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 临江市| 宜丰县| 嘉兴市| 天气| 博白县| 科尔| 沿河| 隆化县| 抚顺市| 固阳县| 洛隆县| 黄梅县| 梨树县| 安塞县| 弥渡县| 井研县| 铜山县| 宽甸| 金寨县| 迁西县| 阿坝县| 枣阳市| 顺义区| 连山| 牟定县| 当阳市| 灵璧县| 历史| 牡丹江市| 绍兴市| 子洲县| 乌审旗| 蛟河市| 岳西县| 遂昌县| 班戈县| 陕西省| 民和| 桃园市| 玛沁县| 宁国市|