正確的equals實現
本文來自圖靈社群 fairjm
轉截請註明出處
首先是equals
方法的Java API上的說明
- It is reflexive: for any non-null reference value x, x.equals(x) should return true.
- It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
- It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
- It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
- For any non-null reference value x, x.equals(null) should return false.
遵循以上的原則是實現equals
方法所必須的。
先說一下最後一條,也就是如果obj == null
就直接返回false
。
第一條自反性,實現這一條最簡單的方式就是用 this == obj
如果為true
直接返回,沒什麼好說的。
第二條是對稱性,x.equals(y)
的返回要和x.equals(y)
的返回保持一致,那怎樣實現會不一致呢?這裡舉個例子,多型判等下:
package test;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
//省略getter setter hashCode
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
Person other;
if (obj instanceof Person) {
other = (Person) obj;
} else {
return false;
}
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
Worker:
package test;
public class Worker extends Person {
private int workHours;
public Worker(String name, int age, int workHours) {
super(name, age);
this.workHours = workHours;
}
// 省略getter setter hashCode
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
Worker other;
if (obj instanceof Worker) {
other = (Worker) obj;
} else {
return false;
}
if (workHours != other.workHours)
return false;
return true;
}
public static void main(String[] args) {
Person p = new Person("cc", 20);
Worker w = new Worker("cc", 20, 0);
System.out.println(w.equals(p));
System.out.println(p.equals(w));
}
}
Worker
繼承Person
,多了一個屬性。上面程式碼的返回是:
false
true
和規範相悖,處理也很容易。把instanceof
改為判斷它們的class是否相等即可:if (obj.getClass() == getClass())
。不同類的例項不應該相等,因為不同類的例項相等要做到自反是挺麻煩的,除非他們能擁有一樣的屬性,但如果兩個類的屬性都一樣那為什麼不把他們合併用一個類呢?
其實有了前兩條的規約和以上的實現,第三條可以不嚴謹推匯出來:
x.equals(y) == true
有兩種情況,x == y(引用相等)或者x,y是同一個類的例項且屬性相等
若 x == y 則,
y.equals(z)
->x.equals(z)
若 x,y同屬於一個類的例項且屬性相等 :
y.equals(z) == true
-> y == z(引用相等) 或者 y,z是同一個類的例項且屬性相等y == z :
x.equals(y)
->x.equals(z)
y,z是同一個類的例項且屬性相等 : x,y,z是同一個類的例項且屬性相等 ->
x.equals(z) == true
第四條因為在判等上就是用的屬性和物件本身的資訊,在物件內的資訊不發生改變的情況下,運算保持一致,除非手欠在裡面用了一些會隨時間或呼叫次數發生改變的方法,簡單地說,就是保持equals
方法是個純函式,只對傳入的引數進行運算而不改變任何狀態。
最簡單的實現方式就是讓IDE自動生成equals
和hashCode
,在自己實現地情況下一定要注意對稱性是否滿足,因為它也是最容易出錯的。
相關文章
- 正確的折半查詢實現
- 正確實現 IDisposable------
- Redis分散式鎖的正確實現方式Redis分散式
- 實現Flutter彈窗的正確姿勢..Flutter
- 如何正確實現 Java 中的 HashCodeJava
- 分散式鎖實現的正確開啟方式分散式
- 如何正確實現Java中的hashCode方法Java
- 這才是實現分散式鎖的正確姿勢!分散式
- 「分散式」實現分散式鎖的正確姿勢?!分散式
- TensorFlow中RNN實現的正確開啟方式RNN
- 在iOS中如何正確的實現行間距與行高iOS
- [譯] 正確實現 linkedPurchaseToken 以避免重複訂閱
- 如何快速又正確地在C++裡實現鎖C++
- Java排行榜中多級排序的一種正確實現方式Java排序
- 併發基礎-第01章-實現執行緒的正確方式執行緒
- RoR的正確定位
- redis應用系列一:分散式鎖正確實現姿勢Redis分散式
- Redis 分散式鎖的正確實現原理演化歷程與 Redission 實戰總結Redis分散式
- DDD實體值物件的equals和hashcode方法實現 - wimdeblauwe物件
- 【Practical Java】實踐10:不要依賴equals()的預設實現Java
- 層疊注意力模型:實現機器閱讀的正確姿勢模型
- 層疊注意力模型 - 實現機器閱讀的正確姿勢模型
- 在現代引擎中使用正確的渲染打光流程
- 如何實現 mysql 匯出資料,驗證頁面正確性?MySql
- 中國菜刀使用(實戰正確姿勢)
- PHP Opcache 的正確使用PHPopcache
- LongTree的正確分析理解
- Go 1.16 io/fs 設計與實現及正確使用姿勢Go
- 如何正確且高效實現OSSIM中文化的解決方案(圖文詳解)
- WikiPedia 的正確開啟方式
- 正確理解 PHP 的過載PHP
- Redis的正確使用姿勢Redis
- API 演進的正確方式API
- 日期的正確儲存方式
- JS 繼承的正確操作JS繼承
- 如何正確的建立網站網站
- Redis的正確安裝方式Redis
- 如何正確的停止MongoDB程式MongoDB