[英]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.