咱們知道Object類中全部的非final方法equals、hashcode、toString、clone、finalize都有明確的約定,由於這些方法就是設計用來被子類覆蓋的。若是不能按照約定覆蓋,那麼其餘依賴這些方法的的類就沒法正常工做,好比HashMap和HashSet。java
咱們先來討論什麼狀況下不用覆蓋equals方法:數組
上述四種狀況咱們能夠不用考慮覆蓋equals方法,但有些類不免仍是要覆寫,讓咱們先看看有哪些約定的內容:性能
對咱們來講上面的每一條都是很簡單的,甚至認爲原本就該這樣。但事實上當咱們要同時知足上述五條,有時候確是一個幾乎不可能完成的任務。優化
自反性就是對於任何非null的引用值x,x.equals(x)必須返回true。這個應該很好理解,若是違反的話,最簡單的你往集合裏添加東西,而後調用contains,你會發現他會告訴你集合不包含你剛給添加的實例。.net
對於非空引用x,y,若是x.equals(y)返回true,那麼y.equals(x)也必須返回true。簡單來講就是x等於y,那麼y就要等於x。舉個例子看看違反的狀況:若是x是一個不去分大小寫的自定義字符串類的一個實例,那麼x假設爲「hello」,y爲普通字符串」Hello「那麼x.equals(y)應該返回true(x是自定義類,equals方法中調用了equalsIgnoreCase)。可是y.equals(x)返回false。設計
如今咱們把x放到集合中,而後list.contains("Hello")會返回什麼。true or false?code
true也好false也好,你把但願都寄託在list.contains的內部實現上,內部遍歷每一個元素時,是使用x.equals(y),仍是y.equals(x),並且這還會致使另外一個問題,及時如今能夠正確運行,那麼將來的實現改變了怎麼辦,你的代碼就不受你控制了。對象
傳遞性就是x等於y,y等於z,那麼x也會等於z。一樣,咱們也舉個例子來講明這個問題。如今咱們有一個Point類,座標系中的一個二維點,它的equals方法應該是比較該點的座標(x,y),若是座標相同則爲同一個點。如今咱們想要表示一個有顏色的點,咱們繼承了Point類,如今考慮equals方法。繼承
首先父類的equals方法能用嗎?答案很明顯,不能。若是用父類的equals方法,那麼顏色就不會比較,紅點就會和綠點相同。因而咱們覆寫equals方法,加上顏色比較,問題結束了嗎?接口
若是咱們比較有色點和無色點會發生什麼呢?父類會根據座標判斷是否相對,而子類只會返回false,由於缺乏顏色信息,這樣會不知足對稱性的要求。咱們再一次修正這個問題。咱們在子類equals內部先判斷類型(instanceof),若是是和父類型Point比較則不考慮顏色信息,和同類型ColorPoint則考慮顏色。此次把問題解決了嗎?
咱們再來考慮一種特殊狀況,紅點(ColorPoint),綠點(ColorPoint),點(Point)。假設三個點在同一個位置上,上面的方法咱們會的到紅點等於點,點等於綠點,可是紅點不等於綠點。你是否是發現了什麼,是的,咱們又違反了傳遞性。
咱們還能夠繼續改進用getClass判斷類型,讓不一樣類型比較都會返回false,即便是子類和父類比。可是這種方式也會帶來一些麻煩,它會限制父類的覆用。
咱們改如何辦呢?事實上這是面嚮對象語言中關於等價關係的一個基本問題:咱們沒法在擴展可實例化的類的同時,既增長新的值組件,同時又保留equals約定,除非願意放棄面向對象的抽象帶來的優點。
在這種狀況下通常使用組合,讓ColorPoint持有Point是一種不錯的選擇,這種方法有點相似於getClass方法,但能夠不用考慮繼承帶來的一些其餘方面的反作用。
對於上述狀況咱們使能夠避免的,咱們能夠把父類設計成接口或者抽象類,只要父類不和子類混用就不會出現問題。
簡單來講就是相等的對象永遠相等,不相等的永遠不相等。要想知足這一點只要在equals中不要用不可靠的資源就好了。例如,java.net.URL這個類的equals方法使用了IP地址,主機名會映射到ip,也就是隨着時間的推移equals結果可能發送變化。
若是不處理null,那麼equals裏會拋出異常。