简体   繁体   English

java.util.AbstractMap.equals():try / catch是什么?

[英]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))可能引发ClassCastExceptionNullPointException

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM