[英]java.util.AbstractMap.equals() : what is it for try/catch?
While writing my own custom MultiHashMap, multiTreeMap etc. classes (yes, I know these are available in Guava Library, but I needed to provide some different functionalities, so I completely rewrote these from scratch) I came across the need to write an equals() method that could compare any MultiMap, returning true if and only if the entrySet's of two MultiMaps are equivalent (same key-value mappings, regardless of the order). 在编写自己的自定义MultiHashMap,multiTreeMap等类时(是的,我知道Guava库中提供了这些类,但是我需要提供一些不同的功能,所以我从头开始完全重写了这些功能)遇到了编写equals( )可以比较任何MultiMap的方法,当且仅当两个MultiMap的entrySet相等时才返回true(相同的键值映射,而不考虑顺序)。
As I hold the multi-values in an ordinary Map I compared my own method with API method java.util.AbstractMap.equals(), and they turned out to be pretty similar, except that I didn't use any try/catch (Java 7): 当我将一个多值保存在一个普通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;
}
The caught exceptions are RuntimeException's and beside that I can't really figure out under which circumstances they may occur. 捕获的异常是RuntimeException的异常,除此之外,我无法真正弄清它们可能在哪种情况下发生。
Any hint ? 有什么提示吗?
They use catching exceptions to make the equals()
code shorter. 他们使用捕获异常使
equals()
代码更短。 I don't think it's a good practice but it works. 我认为这不是一个很好的做法,但是可以。 They replace many
if
-checks by catching the exceptions. 它们通过捕获异常来替换许多
if
-check。
Have a look at an example of auto-generated equals()
method by Eclipse: 看一下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;
}
}
That's a correct way of implementing equals()
to fully fulfill its contract . 这是实现
equals()
以完全履行合同的正确方法。 Now notice that in all the cases when some test for a proper type or for a null fails, the equals()
method returs false
. 现在请注意,在所有情况下,如果针对正确类型或null的某些测试失败,则
equals()
方法将返回false
。 So the idea in the code you provided is to ommit all the checks and just catch the exception. 因此,您提供的代码中的想法是省略所有检查并仅捕获异常。 Something like this:
像这样:
@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;
}
As you may see, the code does the same but is significantly shorter. 如您所见,该代码执行相同的操作,但是明显更短。 However, it is usually considered as bad practice.
但是,通常认为这是不好的做法。 Still I must admit that the code is better readable :)
我仍然必须承认代码可读性更好:)
The reason why it is considered as bad practice is very well described in Joshua Bloch's Effective Java , Item 57: Use exceptions only for exceptional conditions: 约书亚·布洛赫(Joshua Bloch)的《 有效的Java》 ( Effective Java )项目57:仅在特殊情况下使用例外:
Exceptions are, as their name implies, to be used only for exceptional conditions;
顾名思义,异常仅用于特殊情况; they should never be used for ordinary control flow.
它们绝对不能用于普通的控制流程。
I think, the catch is meant to catch erroneous implementations of the equals
method in the V
type. 我认为,catch旨在捕获
V
类型中equals
方法的错误实现。 The call value.equals(m.get(key))
may throw ClassCastException
and or NullPointException
, when equals
is implemented naively in V
. 当在
V
天真地实现equals
时,调用value.equals(m.get(key))
可能引发ClassCastException
和NullPointException
。
A bad implementation of equals
in an actual V
-Parameter type, that would be catched nicely: 在实际的
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
}
}
The answer appears to be pretty simple: because Map.containsKey
method may throw both of these exceptions. 答案似乎很简单:因为
Map.containsKey
方法可能会抛出这两个异常。
From documentation of Map interface: 从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);
Although containsKey implementation in AbstractMap
doesn't actually throw these exceptions, some custom implementations may eventually do this. 尽管
AbstractMap
中的containsKey实现实际上并没有引发这些异常,但是某些自定义实现最终可能会这样做。 And the most reliable way to handle these exceptions is to wrap containsKey
in try-catch block. 处理这些异常的最可靠方法是将
containsKey
包装在try-catch块中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.