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

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

除非您愿意放弃面向对象抽象的好处,否则无法扩展可实例化的类并在保留equals合同的同时添加值组件。

这是Joshua Bloch撰写的“Effective Java”的引用,在这篇伟大的Artima文章中有关于该主题的更多内容:

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

我们完全没问题,这不是这个问题的意思。

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

注释会起作用吗? 我正在考虑像@NotNull这样的事情:每个@NotNull合同违规都会自动抛出一个exception,除了用@NotNull注释你的参数/返回值之外别无他法。

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

在我关注的情况下,在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是否有效?

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

为什么不让IDE(Eclipse / NetBeans / IntelliJ)为您生成hashCode()equals()方法。 他们在这方面做得很好。

当然,AOP会起作用,但这很复杂。 这意味着您将无法使用这些对象几乎没有集合或实用程序。

另一个逻辑解决方案是只删除那些不起作用的方法的实现,这样就可以有效地只留下Object的实现。

我同意你对hashCode的问题的评估,并且首先在Object中定义equals 。 我一直认为应该按照与排序相同的方式处理相等 – 使用一个界面说“我可以与一个X实例进行比较”,另一个说“我可以比较两个X实例”。

另一方面,这实际上是否为您造成了任何错误? 有人一直在尝试使用equalshashCode吗? 因为即使您可以使代码库中的每个类都在不适当地调用这些方法时抛出exception,对于您正在使用的其他类,无论是来自JDK还是第三方库,都不会这样。

我确信你可以用某种forms或其他forms的AOP来做这件事,无论是正常的注释处理还是别的 – 但你有证据表明奖励是值得的吗?

查看它的另一种方式:这只是在你扩展另一个已经覆盖hashCode并且equals类的情况下,对吧? 否则,您可以使用Object的hashCode / equals方法的“equality = identity”特性,这仍然有用。 你有很多属于这一类的课程吗? 您是否可以通过reflection编写unit testing来查找所有这些类型,并在调用hashCode / equals时检查这些类型是否会抛出exception? (如果它们具有无参数构造函数,或者具有已检查的类型的手动列表,则可以自动化 – 如果存在不属于“已知商品”列表的新类型,则unit testing可能会失败。)

我不明白为什么你认为“在某些情况下你不能满足平等()合同”? 等式的含义由类定义。 因此,使用Object等于完全有效。 如果您没有覆盖equals,那么您将每个实例定义为唯一。

似乎存在一种误解,即equals是总是需要重写的方法之一,并且它必须检查其所有字段。 我认为相反 – 除非你的平等定义不同,否则不要重写等于。

我也不同意artima的文章,特别是“陷阱#3:根据可变领域定义平等”。 对于一个基于可变字段定义其相等性的类来说,它是完全有效的。 用户在使用集合时要注意这一点。 如果一个可变对象在其可变状态上定义了它的相等性,那么在一个实例改变之后不要指望两个实例是等于的。

我认为抛出UnsupportedOperation违反了平等的冲刺。 对象等于状态:

类Object的equals方法实现了对象上最具辨别力的等价关系; 也就是说,对于任何非空引用值x和y,当且仅当x和y引用同一对象时,此方法才返回true(x == y的值为true)。

因此,我应该能够调用equals并获取true或false值,具体取决于Object的equals定义或重写的equals定义。

在Java或.NET中,可以在所有对象之间定义至少两个等价关系:

  • 如果使用对Y的引用覆盖X将不会改变X或Y的任何成员的当前或未来行为,则两个对象引用X和Y完全等效。

  • 如果在一个程序中没有持久保存从与身份相关的哈希函数返回的值,那么两个对象引用X和Y具有等效状态,将所有对X的引用交换为所有对Y的引用将使程序状态保持不变。

我有一个参考( X )指向FordBlazer。 我有另一个( Y )指向一个SiameseCat。 它们是等价的吗? 不,他们不是,所以X.equals(Y)应该是假的。 对象的类型彼此之间没有关系的事实不是问题 – 如果有的话,它会使事情变得更容易(如果唯一可以等同于SiameseCat的是另一个SiameseCat,那么事实上Y不是SiameseCat意味着X.equals()不必检查任何其他内容。

虽然关于特定对象是否应该实现等价的第一个或第二个定义可能存在争议,但值得注意的是,任何定义equals对象都将不同的对象报告为不相等而不管其状态的任何其他方面将与其自身一致(如果X.Equals(X)与X.Equals(Y)不匹配,这意味着Y的行为与X不相同。 因此,如果一个人没有与equals有更好的关系,那么从objectinheritance的默认定义是一个非常好且有意义的定义。

hashCode可能遇到问题的唯一情况是,当代码存储在HashTable中时,代码可能(不明智地)改变对象的某些方面。 对此的适当补救措施是使hashCode不依赖于对象状态的任何可变方面。 如果一个对象的状态除了它的类之外没有任何有意义的不可变方面,那么只需为该类组成一个任意数字,并让hashCode始终返回该状态。 大型哈希表对这些对象的性能很差,但是小的哈希码可以正常工作。 无法为类型定义良好哈希代码的事实不应该阻止它在具有十几个项目的HashTable中使用。