Overriding equals () and hashCode () in subclasses ... considering superfields

Is there a specific rule on how to override equals() and hashCode() in subclasses taking into account the superfield ? knowing that there are many parameters: the superfield is private / public, with / without getter ...

For example, Netbeans generates equals (), and hashCode () will not consider the superfield ... and

  new HomoSapiens("M", "80", "1.80", "Cammeron", "VeryHot").equals( new HomoSapiens("F", "50", "1.50", "Cammeron", "VeryHot")) 

will return 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; } ... } 

If you want Netbeans to generate equals () and 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; } } 
+49
java equals override hashcode inheritance
Jan 14 '10 at 19:26
source share
9 answers

Children should not study the private members of their parents.

But obviously, all significant fields must be considered for equality and hashing.

Fortunately, you can easily follow both rules.

Assuming you are not stuck with NetBeans-generated equal and hashcode, you can modify the hominidae equals method to use instanceof comparisons rather than class equality, and then use it directly. Something like that:

 @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 } 

Of course, hashcode is easy:

 @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; } 

Seriously though: what about NetBeans, not taking field superclasses into account, invoking superclass methods?

+52
Jan 14 '10 at 20:41
source share

I prefer to use EqualsBuilder (and HashcodeBuilder) from the commons-lang package to make it easier to read the equals () and hashcode () methods.

Example:

 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(); } 
+20
Jan 14 '10 at 19:42
source share

Generally speaking, implementing equals in subclasses is difficult to maintain symmetrically and transitively.

Consider a superclass that checks the field x and y , and a subclass checks for x , y and z .

So, subclass == Superclass == Subclass, where z differs between the first instance of the subclass and the second, violating the transitive part of the contract.

This is why a typical equals implementation will check getClass() != obj.getClass() instead of doing instanceof. In the above example, if SubClass or Superclass performs instance validation, this will break the symmetry.

So, the result is that the subclass can certainly take super.equals () into account, but must also do its own getClass () check to avoid the above problems, and then further check for equal fields. It would be a weird class duck that would change its own behavior in the same way based on the specific fields of the superclass, and not just if the superclass returns equal.

+8
Jan 14 '10 at 20:02
source share

Rules:

  • This is reflective: for any non-zero reference value x, x.equals (x) should return true.
  • It is symmetric: for any non-empty reference values ​​x and y x.equals (y) should return true if and only if y.equals (x) returns true.
  • This is transitive: for any non-empty reference values ​​x, y and z, if x.equals (y) returns true and y.equals (z) returns true, then x.equals (z) should return true.
  • This is consistent: for any non-empty reference values ​​x and y, several calls to x.equals (y) successively return true or successively return false if the information used in equal comparisons with objects does not change.
  • For any non-zero reference x, x.equals (NULL) should return false.
  • As a general rule, you must override the hashCode method whenever this method is overridden in order to maintain a common contract for the hashCode method, which states that equal objects must have the same hash codes.

from Object.equals () .

So, use the fields necessary to execute the rules.

+4
Jan 14 '10 at 19:33
source share

Because inheritance interrupts encapsulation, subclasses that implement equals () and hashCode () must be aware of their superclasses. I had successful coding calls to the methods of the parent class equals () and hashCode () from the methods of the subclass.

+2
Jan 14 '10 at 19:48
source share

Regarding the accepted answer by @CPerkins, I don’t think that this equals () code will work reliably, due to the likelihood that the super.equals () method will also check for equality of classes. The subclass and superclass will not have equal classes.

+2
May 10 '13 at 16:46
source share

It looks like your parent (super) class is not overriding equals. If so, you need to compare the fields from the parent class when you override this method in a subclass. I agree that using EqualsBuiler's shared resources is the way to go, but you need to be careful that you don't break the symmetry / transactional parts of an equal contract.

If your subclass adds attributes to the parent class, and the parent class is not an abstract and overrides, then you will run into a problem. In this case, you really should look at the composition of the object instead of inheritance.

I would highly recommend this section in Effective Java by Joshua Block. It is comprehensive and very well explained.

+1
Jan 14 '10 at 22:57
source share

Well, HomoSapiens#hashcode will be enough with CPerkins answer.

 @Override public int hashCode() { int hash = super.hashCode(); hash = 89 * hash + Objects.hash(name); hash = 89 * hash + Objects.hash(faceBookNickname); return hash; } 

If you want these parent fields ( gender , weight , height ) in action, one way creates the actual instance of the parent type and uses it. Thank God this is not an abstract class.

 @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; } 

I am adding a way (I think) to solve this. The key point is to add a method that freely checks equality.

 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; } 

And here comes the 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; } 

Testing...

 @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()); } 
+1
Aug 20 '16 at 15:25
source share

It is worth noting that the IDE auto-generation may have taken into account the superclass only if the equals () and hashCode () of the superclass still exist. That is, you should automatically generate these two super functions first, and then automatically generate the child. I came across the correct example in 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; } 

The problem arises when you do not automatically generate super in the first place. Please check above in the Netbeans section.

0
Sep 08 '16 at 6:32
source share



All Articles