Annotation.equals() 与 Object.equals()

Annotation.equals() vs. Object.equals()

某些框架(例如 guice)在某些情况下需要创建 实现 class 注释接口

Annotation.equals(Object) and Object.equals(Object) definitions which need to be respected in that case (same applies for hashCode()) 之间似乎有 差异

问题:

更新:

其他问题:

定义没有区别。 Annotation中的定义只是specialized注解类型。

Object中的定义基本上是"If you decide to implement equals for your class, it should represent an equivalence relation that follows these rules".

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

事实上,Annotation 等效项适用于许多其他 classes。关键是不同的classes有不同的含义,因此它们的实例可能有不同的等价关系,his/herclass使用哪种等价关系由程序员决定。在Annotation中,契约就是针对这个特定的等价关系。

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

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


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

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

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

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

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

举个例子,想象一个不带参数的注解 @SomeAnnotation。想象一下,您使用 class SomeAnnotationImpl 和 return 的 15 作为哈希码来实现它。 SomeAnnotationImpl 的两个相等实例将具有相同的哈希码,这很好。但是 Java 编译器会 return 0 作为散列码,当你检查它自己的 @SomeAnnotation 实现的 returned 实例时。因此 Annotation 类型的两个对象是相等的(它们实现相同的注解接口,如果它们遵循上面的 equals() 定义,它们应该 return true for equals ), 但具有不同的哈希码。那违反了合同。

RealSkeptic 的回答很好,但我会换一种稍微不同的方式。

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

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

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

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

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

  5. 如果 Java 让你实例化它们的实现,或者为你的 class 自动生成一个实现,那会容易得多,但他们在这里做的最好的是为您匹配的确切规格。

所以是的,你会 运行 更频繁地使用注释而不是其他任何东西,但这些在任何时候你试图与你无法控制或使用自己的实现平等对待时都很重要。

以上答案是对问题的一般性回答,但由于我没有看到它们被提及,所以我只想补充一点,使用 AnnotationLiteral 来实现注释可以正确处理 equals 和 hashCode 问题。有一对可供选择:

AnnotationLiteral AnnotationLiteral