[英]java.util.AbstractMap.equals() : what is it for try/catch?
在編寫自己的自定義MultiHashMap,multiTreeMap等類時(是的,我知道Guava庫中提供了這些類,但是我需要提供一些不同的功能,所以我從頭開始完全重寫了這些功能)遇到了編寫equals( )可以比較任何MultiMap的方法,當且僅當兩個MultiMap的entrySet相等時才返回true(相同的鍵值映射,而不考慮順序)。
當我將一個多值保存在一個普通Map中時,我將自己的方法與API方法java.util.AbstractMap.equals()進行了比較,結果發現它們非常相似,除了我不使用任何try / catch( Java 7):
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Map))
return false;
Map<K,V> m = (Map<K,V>) o;
if (m.size() != size())
return false;
try {
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,V> e = i.next();
K key = e.getKey();
V value = e.getValue();
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
捕獲的異常是RuntimeException的異常,除此之外,我無法真正弄清它們可能在哪種情況下發生。
有什么提示嗎?
他們使用捕獲異常使equals()
代碼更短。 我認為這不是一個很好的做法,但是可以。 它們通過捕獲異常來替換許多if
-check。
看一下Eclipse自動生成的equals()
方法的示例:
public class Person {
final private String firstName;
final private String lastName;
...
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Person other = (Person) obj;
if (firstName == null) {
if (other.firstName != null) {
return false;
}
}
else if (!firstName.equals(other.firstName)) {
return false;
}
if (lastName == null) {
if (other.lastName != null) {
return false;
}
}
else if (!lastName.equals(other.lastName)) {
return false;
}
return true;
}
}
這是實現equals()
以完全履行合同的正確方法。 現在請注意,在所有情況下,如果針對正確類型或null的某些測試失敗,則equals()
方法將返回false
。 因此,您提供的代碼中的想法是省略所有檢查並僅捕獲異常。 像這樣:
@Override
public boolean equals(Object obj) {
try {
// Ommit any type-checks before type-casting
// and replace them with catching ClassCastException:
final Person other = (Person) obj;
// Ommit any null-checks before using the references
// and replace them with catching NullPointerException:
if (firstName.equals(other.firstName)
&& lastName.equals(other.lastName)) {
return true;
}
}
catch (ClassCastException | NullPointerException unused) {
// swallow the exception as it is not an error here
}
return false;
}
如您所見,該代碼執行相同的操作,但是明顯更短。 但是,通常認為這是不好的做法。 我仍然必須承認代碼可讀性更好:)
約書亞·布洛赫(Joshua Bloch)的《 有效的Java》 ( Effective Java )項目57:僅在特殊情況下使用例外:
顧名思義,異常僅用於特殊情況; 它們絕對不能用於普通的控制流程。
我認為,catch旨在捕獲V
類型中equals
方法的錯誤實現。 當在V
天真地實現equals
時,調用value.equals(m.get(key))
可能引發ClassCastException
和NullPointException
。
在實際的V
參數類型中對equals
的錯誤實現很容易被發現:
class Whatever {
private int attr;
/* ... */
@Override public boolean equals(Object o) {
Whatever w= (Whatever)o; // possible ClassCastException
return (this.attr == w.attr); // possible NullPointerException
}
}
答案似乎很簡單:因為Map.containsKey
方法可能會拋出這兩個異常。
從Map界面的文檔中:
/**
* ....
* @throws ClassCastException if the key is of an inappropriate type for
* this map (optional)
* @throws NullPointerException if the specified key is null and this map
* does not permit null keys (optional)
*/
boolean containsKey(Object key);
盡管AbstractMap
中的containsKey實現實際上並沒有引發這些異常,但是某些自定義實現最終可能會這樣做。 處理這些異常的最可靠方法是將containsKey
包裝在try-catch塊中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.