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

时间:2014-05-30 10:33:30

标签: java exception-handling

在编写我自己的自定义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,除此之外我无法确定它们可能出现在哪种情况下。

任何提示?

3 个答案:

答案 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可能会抛出equalsV

实际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块中。