[英]Preferred Equals() Method Implementation
這是一個關於當我需要在列表中找到 object 的實例時如何實現 equals 方法的問題,給定我在其成員中擁有的實例之一的值。
我有一個 object,我在其中實現了 equals:
class User {
private String id;
public User(id) {
this.id = id;
}
public boolean equals(Object obj) {
if (!(obj instanceof User)) {
return false;
}
return ((User)obj).id.equals(this.id);
}
}
現在,如果我想在列表中找到一些東西,我會這樣做:
public function userExists(String id) {
List<Users> users = getAllUsers();
return users.contains(new User(id));
}
但也許這可能是更好的實施方式?
class User {
private String id;
public boolean equals(Object obj) {
if (!(obj instanceof User)) {
return false;
}
if (obj instanceof String) {
return ((String)obj).equals(this.id);
}
return ((User)obj).id.equals(this.id);
}
}
有了這個:
public function userExists(String id) {
List<Users> users = getAllUsers();
return users.contains(id);
}
以第二種方式進行是危險的,因為它破壞了等式的對稱性。
Java 期望equals()
的實現是自反的、對稱的和可傳遞的。 第二種實現打破了對稱性:如果將User
與表示其 ID 的String
進行比較,您將得到true
,但如果將字符串與 user 進行比較,則會得到false
。
不要為數學上不相等的事物覆蓋等於。
你可能認為這樣做是個好主意
User bob = new User("Bob");
if (bob.equals("Bob")) {
...
}
但它很少是。 當Strings
與Users
“相等”時,您是否希望所有 equals 觀察代碼都感到困惑?
如果你想要一個查找方法,寫它
class User {
private String id;
public boolean equals(Object obj) {
if (obj instanceof User) {
User other = (User)obj;
if (id.equals(other.id)) {
return true;
}
}
return false;
}
public String getId() {
return id;
}
}
然后其他地方的代碼維護“快速查找”表。
Map<String, User> idTable = new HashMap<String, User>();
User bob = new User("Bob");
idTable.put(bob.getId(), bob);
public User findUser(String id) {
return idTable.get(id);
}
請注意,這不會與 equals 實現混淆,因此現在您可以安全地擁有Sets
of Users
、 Lists
of Users
等,而不必擔心 String 是否會以某種方式破壞工作。
現在如果你找不到一個好的地方來維護一個Map
的Users
indexed by their id
,你總是可以使用更慢的Iterator
解決方案
List<User> users = new List<User>();
users.add(new User("Bob"));
users.add(new User("Steve"));
users.ass(new User("Ann"));
public User findUser(String id) {
Iterator<User> index = users.iterator();
while (index.hasNext()) {
User user = index.next();
if (id.equals(user.getId())) {
return user;
}
}
return null;
}
首先,您的第二個實現在功能上等同於第一個,因為String
的實例不是User
的實例,並且第一個return
語句將在檢查String
之前將其短路。
我的意思是,
public boolean equals(Object obj) {
if (!(obj instanceof User)) { // This will execute if obj is a String
return false;
}
if (obj instanceof String) {
// Never executes, because if obj is a String, we already
// returned false above
return ((String)obj).equals(this.id);
}
return ((User)obj).id.equals(this.id);
}
因此,對於其余的答案,我將假設其意思是
public boolean equals(Object obj) {
if ( obj == null ) return false; // Add a null check for good measure.
if (!(obj instanceof User)) {
if (obj instanceof String) {
// Now we're checking for a String only if it isn't a User.
return ((String)obj).equals(this.id);
}
return false;
}
return ((User)obj).id.equals(this.id);
}
現在我們來解決實際問題。
實現一個為User
到String
比較返回true
的equals
是不好的做法,因為equals
應該是對稱的( a.equals(b)
當且僅當b.equals(a)
)。 並且由於String
永遠不能等於User
,所以User
永遠不應該等於String
。
如果您覆蓋 Object.equals(),請不要忘記覆蓋 Object.hashCode()。
相同的對象必須具有相同的 hash 代碼,如果您不遵守合同,您可能會遇到 Collections 的問題。
第二個選擇很糟糕。 這意味着您允許用戶將User
以外的其他內容傳遞給User.equals()
(即,實際上是Object.equals()
當您實際上並沒有嘗試將 String 與 User 進行比較時。所以您實際上違反了equals
應該做什么的合同。
此外,您的答案都不處理空值檢查。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.