Java:调用 hashCode() 和 equals() 时自动抛出 UnsupportedOperationException 的干净方法?

标签 java equals design-by-contract

我们有一个 OO 代码库,在很多情况下 hashcode()equals()根本行不通,主要是以下原因:

There is no way to extend an instantiable class and add a value component while preserving the equals contract, unless you are willing to forgo the benefits of object-oriented abstraction.

这是来自 Joshua Bloch 的“Effective Java”的引述,这里有一篇很棒的 Artima 文章中有更多关于这个主题的内容:

http://www.artima.com/lejava/articles/equality.html

我们对此完全满意,这不是这个问题的目的。

问题是:看到在某些情况下你不能满足 equals() 是事实契约(Contract),什么是自动生成 hashcode() 的干净方法?和 equals()抛出 UnsupportedOperationException?

注解有用吗?我在想类似 @NotNull 的事情:每个@NotNull违反契约(Contract)确实会自动抛出异常,除了用 @NotNull 注释参数/返回值外,您无需做任何其他事情。 .

很方便,因为它是 8 个字符(“@NotNull”)而不是不断重复相同的验证/抛出异常代码。

在我关心的情况下,在每个实现中 hashCode()/equals()没有意义,我们总是重复同样的事情:

@Override
public int hashCode() {
    throw new UnsupportedOperationException( "contract violation: calling hashCode() on such an object makes no sense" );
}

@Override
public boolean equals( Object o ) {
    throw new UnsupportedOperationException( "contract violation: calling equals() on such an object makes no sense" );
}

然而,这很容易出错:我们可能会错误地忘记剪切/粘贴它,这可能会导致用户滥用此类对象(比如试图将它们放入默认的 Java 集合中)。

或者如果不能通过注解来创建这种行为,AOP 会起作用吗?

有趣的是,真正的问题是 hashCode() 的存在和 equals()在 Java 层次结构的顶部,这在很多情况下根本没有意义。但是,我们如何干净利落地处理这个问题呢?

最佳答案

我同意您的评估,这是首先在 Object 中定义的 hashCodeequals 的问题。长期以来,我一直认为应该以与排序相同的方式处理相等性 - 一个接口(interface)说“我可以与 X 的一个实例进行比较”,另一个说“我可以比较两个 X 的实例”。

另一方面,这个实际上给您带来了任何错误吗?人们是否曾尝试在不应该使用的地方使用 equalshashCode?因为即使您可以让您的代码库中的每个类在这些方法被不当调用时抛出异常,您正在使用的其他类也不会如此,无论是来自 JDK 还是第三方图书馆。

我确信您可以使用某种形式或其他形式的 AOP 来做到这一点,无论是正常的注释处理还是其他 - 但您是否有证据表明付出的努力是值得的?

另一种看待它的方式:这在您扩展另一个已经覆盖hashCodeequals的类的情况下, 正确的?否则,您可以使用 Object 的 hashCode/equals 方法的“equality = identity”特性,它仍然很有用。你有很多属于这一类的类(class)吗?你能不能只写一个单元测试来通过反射找到所有这些类型,并检查这些类型在你调用 hashCode/equals 时是否抛出异常? (如果他们有一个无参数的构造函数,这可以是自动的,或者有一个已检查类型的手动列表——如果有一个新类型不在“已知良好”列表中,单元测试可能会失败。)

关于Java:调用 hashCode() 和 equals() 时自动抛出 UnsupportedOperationException 的干净方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2205565/

相关文章:

java - 如何将 ImageView 固定到布局的底部?

java - 没有自然键可用时 equals() 和 hashCode() 的实现?

C中的契约(Contract)设计模式

java - 在 TLS 重新协商期间更改 TrustManager 的接受发行者

java - 网格袋布局不会填充按钮

scala - 在 Scala 中使用选项的惯用方式

scala - scala DSL 中的final == 和!=(等于和不等于)方法的解决方法

java - 如何显示方法是否可能返回 null

r - 在R中检查和记录功能先决条件和后置条件的惯用方式是什么?

java - jak 看起来不再可用,使用 JAXB 创建 kml