在编写我自己的自定义MultiHashMap,multiTreeMap等类时(是的,我知道这些在Guava库中可用,但我需要提供一些不同的功能,所以我从头开始完全重写了这些)我遇到了编写的需要一个可以比较任何MultiMap的equals()方法,当且仅当两个MultiMaps的entrySet相同时才返回true(相同的键值映射,无论顺序如何)。
当我在普通Map中保存多值时,我将自己的方法与API方法java.util.AbstractMap.equals()进行了比较,结果发现它们非常相似,只是我没有使用任何尝试/ 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,除此之外我无法确定它们可能出现在哪种情况下。
任何提示?
答案 0 :(得分:2)
他们使用捕获异常来缩短equals()
代码。我不认为这是一个很好的做法,但它确实有效。它们通过捕获异常来替换许多if
- 检查。
查看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()
实施为fulfill its contract的正确方法。
现在请注意,在对某个类型或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的Effective Java,第57项:仅在特殊条件下使用例外情况,非常好地描述了它被认为是不良做法的原因:
正如其名称所暗示的那样,例外情况仅用于例外情况 条件;它们永远不应该用于普通的控制流程。
答案 1 :(得分:0)
我认为,捕获的目的是捕获equals
类型中V
方法的错误实现。在value.equals(m.get(key))
天真地实施ClassCastException
时,来电NullPointException
可能会抛出equals
和V
。
实际equals
- 参数类型中V
的错误实现,可以很好地捕获:
class Whatever {
private int attr;
/* ... */
@Override public boolean equals(Object o) {
Whatever w= (Whatever)o; // possible ClassCastException
return (this.attr == w.attr); // possible NullPointerException
}
}
答案 2 :(得分:0)
答案似乎很简单:因为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块中。