首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Annotation.equals()与Object.equals()

Annotation.equals()与Object.equals()
EN

Stack Overflow用户
提问于 2015-07-22 13:57:50
回答 3查看 985关注 0票数 2

某些框架(例如盖斯)在某些情况下需要创建注释接口的实现类。

在这种情况下,需要尊重的和Object.equals(Object)定义之间似乎存在着Annotation.equals(Object)Object.equals(Object)的差异( hashCode()也是如此。

问题:

  • 为什么它是这样设计的,差异的原因是什么?
  • 当对注释类使用Object.equals(Object)定义时,会出现什么副作用?

更新:

补充问题:

  • 那么Annotation.hashCode()的定义呢?是否真的需要以这种方式实现它,特别是“(.)127倍于String.hashCode() XOR散列代码(.)计算的成员名称的哈希代码”-part?
  • 如果hashCode()方法实现为与equals()一致,但与Annotation.hashCode()的确切定义不匹配(例如,使用成员名的哈希代码128倍),会发生什么情况?
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-07-22 14:10:34

定义没有什么不同。Annotation中的定义只是为注释类型专门化。

Object中的定义基本上是“如果您决定为您的类实现equals,它应该表示遵循这些规则的等价关系”。

Annotation中,它定义了遵循这些规则的等价性,这对于Annotation实例特别有意义。

事实上,Annotation等价对于许多其他类都是有效的。关键是不同的类有不同的含义,因此它们的实例可能有不同的等价关系,这取决于程序员决定为他/她的类使用哪种等价关系。在Annotation中,契约是针对这种特殊的等价关系的。

至于副作用--假设一个Annotation类型继承了Object的等价物。许多人试图在地图或其他equals()-dependent环境中使用自己的类时,就会犯这个错误。Object有一个与对象标识相同的equals()函数:只有当引用同一个对象时,两个引用才等于。

如果您使用它,那么没有两个实例会被认为是相同的。尽管它们在字段中具有相同的值并在语义上表示相同的行为,但您将无法创建与前一个实例等价的第二个注释实例。因此,当两个条目具有相同注释的不同实例时,您将无法判断它们是否使用相同的注释进行注释。

至于hashCode问题,虽然Jeff已经回答了这个问题,但为了使我的答案更加完整,我将讨论这个问题:

基本上,注释的实现留给编译器,而JLS并没有规定确切的实现。也可以创建实现类,正如您的问题本身所提到的那样。

这意味着注释类可以来自不同的来源--不同的编译器(您应该能够在任何地方运行.class文件,不管哪个java编译器创建它们)和开发人员创建的实现。

equals()hashCode()方法通常在单个类上下文中考虑,而不是在接口上下文中考虑。这是因为接口通常与实现相反--它们只定义契约。当您为特定类创建这些方法时,您知道您所比较的对象应该是同一个类,因此具有相同的实现。一旦它有了一个hashCode方法,该方法对同一类在equals下等效的对象返回相同的值,那么无论该实现是什么,它都满足约定。

但是,在这种特殊情况下,您有一个接口,并且需要使equals()和hashcode()不仅对同一个类的两个实例工作,而且要对实现相同接口的不同类的实例工作。这意味着,如果您不能跨所有可能的类就单个实现达成一致,那么您可能会得到具有相同元素值的同一个注释的两个实例,以及不同的哈希代码。这将违反hashcode()的合同。

例如,假设一个不接受参数的注释@SomeAnnotation。假设您使用一个类SomeAnnotationImpl实现它,该类返回15作为哈希代码。两个相同的SomeAnnotationImpl实例将具有相同的哈希代码,这是很好的。但是,当您检查0自己的@SomeAnnotation实现的返回实例时,Java编译器将返回@SomeAnnotation作为哈希代码。因此,Annotation类型的两个对象是相等的(它们实现相同的注释接口,如果它们遵循上面的equals()定义,它们应该返回true for equals),但是具有不同的哈希代码。这违反了合同。

票数 4
EN

Stack Overflow用户

发布于 2015-07-22 15:18:05

RealSkeptic的回答很棒,但我要说的是稍微不同的方式。

这是一般问题的一个具体实例:

  1. 您定义了一个接口(特别是一个注释)。
  2. 有人(javac)编写了该接口的特定(内置)实现。您不能访问该实现,但需要能够创建相同的实例,特别是用于Sets和Maps。(Guice毕竟是一个很大的Map<Key, Provider>。)
  3. 实现者(javac)编写了等于的自定义实现,以便具有相同参数的注释实例通过equals。您需要匹配该实现,以便equals是对称的(a.equals(b)当且仅当b.equals(a),这是在Java中假设的,以及反射性、一致性和传递性)。
  4. 相等的对象必须有相同的hashCode,因为Java使用它作为相等的快捷方式:如果对象有不相等的hashCodes,那么它们就不能相等。这对于高效的make是很有用的,因为您可以使用hashCode只检查正确的hashCode-determined桶中的对象。如果您使用了不同的或修改过的hashCode算法,理论上您就会违反规范,而在实践中,您的注释实现在HashSet或HashMap中不会与其他实现一致(使它对Guice来说毫无价值)。许多其他特性使用hashCode,但这些都是最明显的例子。
  5. 如果Java允许您实例化它们的实现,或者为您的类自动生成一个实现,那就容易多了,但是在这里,它们所做的最好的是为您提供一个完全匹配的规范。

因此,是的,您会遇到这种情况,注释比任何其他东西都多,但是,无论什么时候,您试图与您无法控制或使用的实现采取相同的操作时,这些都是重要的。

票数 2
EN

Stack Overflow用户

发布于 2015-07-23 12:18:39

上面的答案是对这个问题的很好的一般回答,但是由于我还没有见过它们,我只想补充一点,使用AnnotationLiteral实现注释可以适当地处理相等和hashCode问题。有一对夫妇可以选择:

AnnotationLiteral AnnotationLiteral

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31565309

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档