是否有关于如何覆盖equals()
&的具体规则?考虑超级字段,子类中的hashCode()
知道有很多参数:超级字段是私有/公共的,有/无getter ...
例如,Netbeans生成了equals()& hashCode()不会考虑超级字段......和
new HomoSapiens("M", "80", "1.80", "Cammeron", "VeryHot").equals(
new HomoSapiens("F", "50", "1.50", "Cammeron", "VeryHot"))
将返回true :(
public class Hominidae {
public String gender;
public String weight;
public String height;
public Hominidae(String gender, String weight, String height) {
this.gender = gender;
this.weight = weight;
this.height = height;
}
...
}
public class HomoSapiens extends Hominidae {
public String name;
public String faceBookNickname;
public HomoSapiens(String gender, String weight, String height,
String name, String facebookId) {
super(gender, weight, height);
this.name = name;
this.faceBookNickname = facebookId;
}
...
}
如果你想看到Netbeans生成的等于()& hashCode():
public class Hominidae {
...
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Hominidae other = (Hominidae) obj;
if ((this.gender == null) ? (other.gender != null) : !this.gender.equals(other.gender)) {
return false;
}
if ((this.weight == null) ? (other.weight != null) : !this.weight.equals(other.weight)) {
return false;
}
if ((this.height == null) ? (other.height != null) : !this.height.equals(other.height)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 5;
hash = 37 * hash + (this.gender != null ? this.gender.hashCode() : 0);
hash = 37 * hash + (this.weight != null ? this.weight.hashCode() : 0);
hash = 37 * hash + (this.height != null ? this.height.hashCode() : 0);
return hash;
}
}
public class HomoSapiens extends Hominidae {
...
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final HomoSapiens other = (HomoSapiens) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if ((this.faceBookNickname == null) ? (other.faceBookNickname != null) : !this.faceBookNickname.equals(other.faceBookNickname)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 89 * hash + (this.faceBookNickname != null ? this.faceBookNickname.hashCode() : 0);
return hash;
}
}
答案 0 :(得分:56)
儿童不应该检查父母的私人成员
但显然是,所有重要字段都应考虑到相等和散列。
幸运的是,您可以轻松满足这两个规则。
假设您没有使用NetBeans生成的equals和hashcode,您可以修改Hominidae的equals方法以使用instanceof比较而不是类相等,然后直接使用它。像这样:
@Override
public boolean equals(Object obj) {
if (obj == null) { return false; }
if (getClass() != obj.getClass()) { return false; }
if (! super.equals(obj)) return false;
else {
// compare subclass fields
}
当然,哈希码很简单:
@Override
public int hashCode() {
int hash = super.hashCode();
hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 89 * hash + (this.faceBookNickname != null ? this.faceBookNickname.hashCode() : 0);
return hash;
}
但是,严重的是:NetBeans没有通过调用超类方法来考虑超类字段?
答案 1 :(得分:20)
我更喜欢使用EqualsBuilder中的commons-lang package(和HashcodeBuilder)来使我的equals()和hashcode()方法更容易阅读。
示例:
public boolean equals(Object obj) {
if (obj == null) { return false; }
if (obj == this) { return true; }
if (obj.getClass() != getClass()) {
return false;
}
MyClass rhs = (MyClass) obj;
return new EqualsBuilder()
.appendSuper(super.equals(obj))
.append(field1, rhs.field1)
.append(field2, rhs.field2)
.append(field3, rhs.field3)
.isEquals();
}
答案 2 :(得分:8)
一般来说,跨子类实现equals很难保持对称和传递。
考虑一个检查字段x
和y
的超类,以及x
,y
和z
的子类检查。
所以Subclass == Superclass == Subclass,其中z在Subclass的第一个实例和第二个实例之间是不同的,违反了契约的传递部分。
这就是为什么equals的典型实现将检查getClass() != obj.getClass()
而不是执行instanceof。在上面的示例中,如果SubClass或Superclass执行instanceof检查,它将破坏对称性。
所以结果是一个子类当然可以考虑super.equals(),但也应该进行自己的getClass()检查以避免上述问题,然后另外检查自己字段上的equals。它是一个奇怪的类,它根据超类的特定字段改变了自己的等于行为,而不仅仅是超类返回等于。
答案 3 :(得分:5)
规则是:
。
因此,请使用完成规则所需的字段。
答案 4 :(得分:2)
因为继承会破坏封装,所以实现equals()和hashCode()的子类必然会考虑其超类的特殊性。我已成功编码从子类的方法调用父类的equals()和hashCode()方法。
答案 5 :(得分:2)
关于接受的@CPerkins答案,我认为给定的equals()代码不会可靠地运行,因为super.equals()方法也可能检查类是否相等。一个子类&超类不会有相同的类。
答案 6 :(得分:1)
听起来你的父(超级)类不会覆盖equals。如果是这种情况,则在子类中重写此方法时,需要比较父类中的字段。我同意使用公共EqualsBuiler是要走的路,但是你需要注意不要破坏等于合同的对称/转换部分。
如果您的子类将属性添加到父类,并且父类不是抽象的并且覆盖等于您将会遇到麻烦。在这种情况下,您应该真正关注对象组合而不是继承。
我强烈推荐Joshua Block的Effective Java部分。它非常全面且解释得非常好。
答案 7 :(得分:1)
嗯,HomoSapiens#hashcode
对于CPerkins的回答就足够了。
@Override
public int hashCode() {
int hash = super.hashCode();
hash = 89 * hash + Objects.hash(name);
hash = 89 * hash + Objects.hash(faceBookNickname);
return hash;
}
如果您希望这些父字段(gender
,weight
,height
)正在运行,则一种方法是创建父类型的实际实例并使用它。感谢上帝,它不是一个抽象的类。
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final HomoSapiens other = (HomoSapiens) obj;
if (!super.equals(new Hominidae(
other.gender, other.weight, other.height))) {
return false;
}
if (!Objects.equals(name, other.name)) return false;
if (!Objects.equals(faceBookNickname, other.faceBookNickname))
return false;
return true;
}
我正在添加一种方法(我认为)解决这个问题。关键是添加一个方法松散地检查相等性。
public class Parent {
public Parent(final String name) {
super(); this.name = name;
}
@Override
public int hashCode() {
return hash = 53 * 7 + Objects.hashCode(name);
}
@Override
public boolean equals(final Object obj) {
return equalsAs(obj) && getClass() == obj.getClass();
}
protected boolean equalsAs(final Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (!getClass().isAssignableFrom(obj.getClass())) return false;
final Parent other = (Parent) obj;
if (!Objects.equals(name, other.name)) return false;
return true;
}
private final String name;
}
这是Child
。
public class Child extends Parent {
public Child(final String name, final int age) {
super(name); this.age = age;
}
@Override
public int hashCode() {
return hash = 31 * super.hashCode() + age;
}
@Override
public boolean equals(final Object obj) {
return super.equals(obj);
}
@Override
protected boolean equalsAs(final Object obj) {
if (!super.equalsAs(obj)) return false;
if (!getClass().isAssignableFrom(obj.getClass())) return false;
final Child other = (Child) obj;
if (age != other.age) return false;
return true;
}
private final int age;
}
...测试
@Test(invocationCount = 128)
public void assertReflective() {
final String name = current().nextBoolean() ? "null" : null;
final int age = current().nextInt();
final Child x = new Child(name, age);
assertTrue(x.equals(x));
assertEquals(x.hashCode(), x.hashCode());
}
@Test(invocationCount = 128)
public void assertSymmetric() {
final String name = current().nextBoolean() ? "null" : null;
final int age = current().nextInt();
final Child x = new Child(name, age);
final Child y = new Child(name, age);
assertTrue(x.equals(y));
assertEquals(x.hashCode(), y.hashCode());
assertTrue(y.equals(x));
assertEquals(y.hashCode(), x.hashCode());
}
@Test(invocationCount = 128)
public void assertTransitive() {
final String name = current().nextBoolean() ? "null" : null;
final int age = current().nextInt();
final Child x = new Child(name, age);
final Child y = new Child(name, age);
final Child z = new Child(name, age);
assertTrue(x.equals(y));
assertEquals(x.hashCode(), y.hashCode());
assertTrue(y.equals(z));
assertEquals(y.hashCode(), z.hashCode());
assertTrue(x.equals(z));
assertEquals(x.hashCode(), z.hashCode());
}
答案 8 :(得分:1)
值得注意的是,IDE自动生成可能已经考虑了超类,只要提供了超类的equals()和hashCode()。也就是说,应首先自动生成超级这两个函数,然后自动生成子项。我在Intellj Idea下面得到了正确的例子:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
TActivityWrapper that = (TActivityWrapper) o;
return data != null ? data.equals(that.data) : that.data == null;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (data != null ? data.hashCode() : 0);
return result;
}
当你不首先自动生成super时会出现问题。请在Netbeans下面查看。
答案 9 :(得分:0)
我相信他们现在有一种方法可以帮到你:
EqualsBuilder.reflectionEquals(this,o);
HashCodeBuilder.reflectionHashCode(本);