views:

304

answers:

7

Is there a specific rule on how Overriding equals() & hashCode() in sub classes considering super fields ?? knowing that there is many parameters : super fields are private/public , with/without getter ...

For instance, Netbeans generated equals() & hashCode() will not consider the super fields ... 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 to see the Netbeans generated equals() & 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;
    }
}
A: 

The only rules are this:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null 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.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.
  • Is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes

from Object.equals().

So, use the fileds needed to fulfill the rules.

rodrigoap
+5  A: 

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

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();
}
matt b
looks like a clean solution !
wj
+2  A: 

Because inheritance breaks encapsulation, subclasses that implement equals() and hashCode() must, necessarily, account for the peculiarities of their superclasses. I've had success encoding calls to the parent class's equals() and hashCode() methods from the subclass's methods.

Steve Emmerson
Similarly, overriding `equals()` and `hashCode()` in `Hominidae` will make the corresponding methods in `HomoSapiens` comparatively easier to implement.
trashgod
@trashgod:overriding equals() and hashCode() in Hominidae will not make the corresponding methods in HomoSapiens easier to implement because we can not do something like "super.equals(obj.super)" where obj is the compared object ...
wj
@wj: You're right. I was thinking of this example, which invokes the corresponding methods of `String`: http://stackoverflow.com/questions/1924728/why-isnt-collections-binarysearch-working-with-this-comparable/1926111#1926111
trashgod
@trashgod: in fact I was totally wrong ... and you was absolutely right :)
wj
+1  A: 

Generally speaking implementing equals across subclasses is hard to keep symmetric and transitive.

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

So a Subclass == Superclass == Subclass where z is different between the first instance of Subclass and the second, violating the transitive part of the contract.

This why the typical implementation of equals will check for getClass() != obj.getClass() instead of doing an instanceof. In the above example, if SubClass or Superclass does an instanceof check it would break symmetry.

So the upshot is that a subclass can certainly take into account super.equals() but should also do its own getClass() check to avoid the above issues and then check for equals on its own fields in addition. It would be a strange duck of a class that changed its own equals behavior based on specific fields of the superclass rather than just if the superclass returns equals.

Yishai
thx Yishai, but the issue here is to compare 2 instances of subclass and the problem is that we can not do something like "super.equals(obj.super)" where obj is the compared object
wj
@wj, As long as your class and the obj class are the same, I don't get why you can't call `if (!super.equals(obj)) return false'
Yishai
@Yishai, the point here is to find a way to compare "this.super" and "obj.super" not "this.super" and "obj" because their not direct instance of the same class meaning that "super.equals(obj)" is always false ... in my example "this.super" is an "Hominidae" when "obj" is an "HomoSapiens"
wj
@wj, I don't think you understand me. I'm suggesting the same thing as the appendSuper method in the Equals builder in @matt b's answer.
Yishai
@Yishai, you're right !!
wj
+3  A: 

Angelika Langer covers this pretty in depth here.

james
this is a great link !!
wj
+2  A: 

Children should not examine the private members of their parents

But obviously, all significant fields should be taken into account for equality and hashing.

Fortunately, you you can easily satisfy both rules.

Assuming you're not stuck using the NetBeans-generated equals and hashcode, you can modify Hominidae's equals method to use instanceof comparison rather than class equality, and then use it straightforwardly. Something like this:


    @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's up with NetBeans not taking superclass fields into account by calling the superclass methods?

CPerkins
@CPerkins : it's hard to automatize this generation, think about the case of private fields with/without getter ... I think that's why IDE's don't do it ...
wj
@wj, all you have to do is call the superclass equals and hashcode methods, and the fields are taken into account. IDEs could easily do this. Children should not have to explicitly examine the private members of their parents.
CPerkins
@CPerkins, you're right !!
wj
+1  A: 

It sounds like your parent (super) class doesn't override equals. If this is the case then you need to compare the fields from the parent class when you override this method in the sub-class. I agree that using the commons EqualsBuiler is the way to go but you do need to be careful that you don't break the symmetry/transative portions of the equals contract.

If your sub-class adds attributes to the parent class and the parent class isn't abstract and overrides equals you're going to get in to trouble. In this scenario you should really look at object composition instead of inheritance.

I'd strongly recommend the section in Effective Java by Joshua Block on this. It's comprehensive and really well explained.

BigMikeW