比较 类 扩展相同的超类
Compare classes extending the same superclass
假设我们有一个 class MyEntity
,它有一些带有 getter 和 setter 的字段。此外 classes EntityA
和 EntityB
扩展 MyEntity
。 MyEntityA
中有一些字段不在 MyEntityB
中,反之亦然。当我们谈论实体时,EntityA
和 EntityB
确实有自己的 equals
-方法,看起来像
@Override
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof EntityA))
return false;
EntityA castOther = (EntityA) other;
return ((this.getId() == castOther.getId()) || (this.getId() != null && castOther.getId() != null && this.getId().equals(castOther.getId())));
}
休眠需要此 equals
方法来识别唯一实体。
现在我想将 EntityA
的实例与 EntityB
的实例进行比较。如果 MyEntity
中的所有字段都匹配,我将它们定义为相同。
为了检查这一点,我让 eclipse 为我生成 equals 方法并将其复制到 isEqualWithRegardsToContent(MyEntity other)
.
之类的方法中
我发现这种方法存在一个大问题:
如果有人向其中一个实体添加了一个新列并且没有更新 isEqualWithRegardsToContent(MyEntity other)
方法,它就会出现错误:实体可能被认为在内容方面是平等的,尽管它们不是。
我认为单元测试在这里没有帮助。
您有什么最佳做法吗?
一般来说,在处理继承树时不可能有一个功能齐全的 equals。 superclass.equals(subclass)
不会 return 与 subclass.equals(superclass)
相同的结果,打破对称性(这是平等的基本契约 - 如果 a.eq(b)
则也 b.eq(a)
)。
您可以仅在顶层实现 equals(),从而从超类的角度比较整个层次结构。这会起作用,但不会与 "all fields" (仅超类中的那些)进行比较。通常实体可以仅通过它们的主键进行比较
就我个人而言,我通常不会混合 2 - equals()
和 hashcode()
我保留用于非多态的简单数据存储 类(或用于在地图中查找的键)。
在此处查看详细报道 - http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html
A class' equals()
方法应该只适用于 class 的其他实例。如果您想将 EntityA
和 EntityB
与 equals()
进行比较,那么您的 equals()
方法应该定义在 MyEntity
上(连同 hashCode()
)并且EntityA
和 EntityB
本身都不应该有 equals()
方法。否则,你很有可能运行与contract for equals()
发生冲突。 Hibernate 等库依赖于 equals()
正确遵守合同。
如果我正确理解你的问题:
- 您有一个名为
MyEntity
的 class 缺少 getId
字段
- 你有 2 个子classes
EntityA
和 EntityB
都定义了一个 id
字段但是形成了不同的生成器
- 当比较 class
EntityA
(resp. EntityB
)的 2 个对象时,当且仅当它们的 id 字段具有相同的值时,它们比较相等 - 我假设在那种情况下所有从 MyEntity
继承的字段具有相同的值
- 将
EntityA
对象与 EntityB
对象进行比较时,当且仅当从 MyEntity
继承的所有字段都相等时,它们应该比较相等。
您可以执行以下操作:
- 在
MyEntity
中声明一个 equals
方法,当且仅当所有字段比较相等时 returns 为真 - 如果您不能为此使用 equals
方法,名称方法ABequals
(例如)
在 subclass 中声明 equal 方法 EntityA
(resp. EntityB
)
@Override
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof EntityA))
return MyEntity.equals(other); // or return ABequals(other);
EntityA castOther = (EntityA) other;
return ((this.getId() == castOther.getId()) || (this.getId() != null && castOther.getId() != null && this.getId().equals(castOther.getId())));
}
在那种情况下,您必须在MyEntity
class中实现一个使用所有字段的散列方法,您不应该在子classes。这样你就可以确保比较相等的两个对象将具有相同的哈希值 - 上面的定义已经保证 equals
函数是一个等价关系。
比如说,你在 superclass 中有 equals()
方法比较公共属性。
在subclass equals()
中可以先调用super.equals()
,然后,如果比较的对象也是这个class,只比较特定的属性。所以在 EntityA
你写:
@Override
public boolean equals(Object o) {
boolean eq = super.equals(o);
if (eq && o instanceof EntityA) {
EntityA e = (EntityA) o;
return Objects.equals(this.propOne, e.propOne)
&& Objects.equals(this.propTwo, e.propTwo)
&& // compare other properties
} else
return eq;
}
这样,相同具体 class 的对象将通过全套属性进行比较,包括通用属性和特定属性,而不同 classes 的实例将仅通过通用属性进行比较。虽然这是违反合同传递属性的非标准方式,但它可能会解决您的特定问题。
假设我们有一个 class MyEntity
,它有一些带有 getter 和 setter 的字段。此外 classes EntityA
和 EntityB
扩展 MyEntity
。 MyEntityA
中有一些字段不在 MyEntityB
中,反之亦然。当我们谈论实体时,EntityA
和 EntityB
确实有自己的 equals
-方法,看起来像
@Override
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof EntityA))
return false;
EntityA castOther = (EntityA) other;
return ((this.getId() == castOther.getId()) || (this.getId() != null && castOther.getId() != null && this.getId().equals(castOther.getId())));
}
休眠需要此 equals
方法来识别唯一实体。
现在我想将 EntityA
的实例与 EntityB
的实例进行比较。如果 MyEntity
中的所有字段都匹配,我将它们定义为相同。
为了检查这一点,我让 eclipse 为我生成 equals 方法并将其复制到 isEqualWithRegardsToContent(MyEntity other)
.
我发现这种方法存在一个大问题:
如果有人向其中一个实体添加了一个新列并且没有更新 isEqualWithRegardsToContent(MyEntity other)
方法,它就会出现错误:实体可能被认为在内容方面是平等的,尽管它们不是。
我认为单元测试在这里没有帮助。
您有什么最佳做法吗?
一般来说,在处理继承树时不可能有一个功能齐全的 equals。 superclass.equals(subclass)
不会 return 与 subclass.equals(superclass)
相同的结果,打破对称性(这是平等的基本契约 - 如果 a.eq(b)
则也 b.eq(a)
)。
您可以仅在顶层实现 equals(),从而从超类的角度比较整个层次结构。这会起作用,但不会与 "all fields" (仅超类中的那些)进行比较。通常实体可以仅通过它们的主键进行比较
就我个人而言,我通常不会混合 2 - equals()
和 hashcode()
我保留用于非多态的简单数据存储 类(或用于在地图中查找的键)。
在此处查看详细报道 - http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html
A class' equals()
方法应该只适用于 class 的其他实例。如果您想将 EntityA
和 EntityB
与 equals()
进行比较,那么您的 equals()
方法应该定义在 MyEntity
上(连同 hashCode()
)并且EntityA
和 EntityB
本身都不应该有 equals()
方法。否则,你很有可能运行与contract for equals()
发生冲突。 Hibernate 等库依赖于 equals()
正确遵守合同。
如果我正确理解你的问题:
- 您有一个名为
MyEntity
的 class 缺少getId
字段 - 你有 2 个子classes
EntityA
和EntityB
都定义了一个id
字段但是形成了不同的生成器 - 当比较 class
EntityA
(resp.EntityB
)的 2 个对象时,当且仅当它们的 id 字段具有相同的值时,它们比较相等 - 我假设在那种情况下所有从MyEntity
继承的字段具有相同的值 - 将
EntityA
对象与EntityB
对象进行比较时,当且仅当从MyEntity
继承的所有字段都相等时,它们应该比较相等。
您可以执行以下操作:
- 在
MyEntity
中声明一个equals
方法,当且仅当所有字段比较相等时 returns 为真 - 如果您不能为此使用equals
方法,名称方法ABequals
(例如) 在 subclass 中声明 equal 方法
EntityA
(resp.EntityB
)@Override public boolean equals(Object other) { if ((this == other)) return true; if ((other == null)) return false; if (!(other instanceof EntityA)) return MyEntity.equals(other); // or return ABequals(other); EntityA castOther = (EntityA) other; return ((this.getId() == castOther.getId()) || (this.getId() != null && castOther.getId() != null && this.getId().equals(castOther.getId()))); }
在那种情况下,您必须在MyEntity
class中实现一个使用所有字段的散列方法,您不应该在子classes。这样你就可以确保比较相等的两个对象将具有相同的哈希值 - 上面的定义已经保证 equals
函数是一个等价关系。
比如说,你在 superclass 中有 equals()
方法比较公共属性。
在subclass equals()
中可以先调用super.equals()
,然后,如果比较的对象也是这个class,只比较特定的属性。所以在 EntityA
你写:
@Override
public boolean equals(Object o) {
boolean eq = super.equals(o);
if (eq && o instanceof EntityA) {
EntityA e = (EntityA) o;
return Objects.equals(this.propOne, e.propOne)
&& Objects.equals(this.propTwo, e.propTwo)
&& // compare other properties
} else
return eq;
}
这样,相同具体 class 的对象将通过全套属性进行比较,包括通用属性和特定属性,而不同 classes 的实例将仅通过通用属性进行比较。虽然这是违反合同传递属性的非标准方式,但它可能会解决您的特定问题。