Annotation.equals()与Object.equals()

时间:2015-07-22 13:57:50

标签: java annotations equals guice hashcode

某些框架(例如guice)在某些情况下需要创建实现注释界面的类

Annotation.equals(Object)Object.equals(Object)定义之间似乎存在差异,在这种情况下需要得到尊重(同样适用于hashCode())。< / p>

问题:

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

更新

其他问题:

  • Annotation.hashCode()定义怎么样?是否真的需要以这种方式实现它,特别是&#34; (...)127次由String.hashCode()计算的成员名的哈希码)XOR哈希码(.. 。)&#34; -part

  • 如果实施hashCode()方法与equals()保持一致但与Annotation.hashCode()的确切定义不匹配(例如使用哈希值的128倍),会发生什么情况会员名称的代码)?

3 个答案:

答案 0 :(得分:4)

定义没有区别。 Annotation中的定义只是注释类型的专用

Object中的定义基本上表示&#34;如果您决定为您的班级实施equals,它应该代表符合这些规则的等价关系&#34;。

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

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

至于副作用 - 假设Annotation类型继承Object等于。这是许多人在尝试在地图或其他equals()依赖情况下使用自己的类时所犯的错误。 Object具有与对象标识相同的equals()函数:只有在引用同一对象时,两个引用才相等。

如果您使用了它,则不会将两个实例视为相同。尽管它们在其字段中具有相同的值并且在语义上表示相同类型的行为,但您将无法创建与前一个相同的第二个Annotation实例。因此,当它们具有相同注释的不同实例时,您将无法判断两个项目是否使用相同的注释进行注释。

关于hashCode问题,虽然Jeff Bowman已经回答过这个问题,但我会解决这个问题,以使我的答案更加完整:

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

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

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

但是,在这种特殊情况下,您有一个接口,并且您需要使equals()和hashcode()不仅适用于同一个类的两个实例,而且适用于实现该类的不同类的实例。相同的界面。这意味着如果您不同意所有可能类的单个实现,则可能会获得具有相同元素值和不同哈希码的相同注释的两个实例。这会破坏hashcode()合同。

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

答案 1 :(得分:2)

RealSkeptic的答案很棒,但我会采用稍微不同的方式。

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

  1. 您定义了一个接口(特别是一个注释)。

  2. 有人(javac)编写了该接口的特定(内置)实现。您无法访问该实现,但需要能够创建相同的实例,尤其是在集合和地图中使用。 (Guice毕竟是一个大Map<Key, Provider>。)

  3. 实现者(javac)编写了一个equals的自定义实现,以便具有相同参数的注释实例传递equals。您需要匹配该实现,以便equals是对称的(a.equals(b)当且仅当b.equals(a),这在Java中是假定的,具有反身性,一致性和传递性。)

  4. Equal对象必须具有相同的hashCode s,因为Java将其用作相等的快捷方式:如果对象具有不相等的hashCodes,则它们不能相等。这对于制作有效的Map实现HashMap非常方便,因为您可以使用hashCode仅检查右hashCode个确定存储桶中的对象。如果您使用了不同的或修改过的hashCode算法,那么您在理论上就会破坏规范,而在实践中,您的注释实现在HashSet或HashMap中不会一致地匹配其他(使其对Guice毫无价值) 。许多其他功能使用hashCode,但这些是最明显的例子。

  5. 如果Java允许您实例化他们的实现,或者为您的课程自动生成实现,那将会容易得多,但是他们所做的最好的是您要匹配的确切规范。

  6. 所以,是的,你会更多地使用注释来解决这个问题,但是这些问题无论何时你都试图采用与你自己无法控制或使用的实现相同的行为。

答案 2 :(得分:0)

以上答案是这个问题的优秀一般答案,但由于我没有看到它们的提及,我只想补充一点,使用AnnotationLiteral实现Annotations可以正确处理equals和hashCode问题。有几个可供选择:

AnnotationLiteral AnnotationLiteral