Right way to implement equals contract [duplicate]
Asked Answered
V

4

19

I have a domain object called User. Properties of user include ssoId, name, email, createdBy, createdDate and userRole. Of these, ssoId must be unique in the sense no two users can have the same sso id. So my equals method checks for the sso id and returns either true or false.

@Override public boolean equals(Object o) {
    if (!(o instanceof User))
      return false;
    return user.getSsoId().equals((User)o.getSsoId());
}

What I feel is that this is an incorrect implementation, though it is correct as far as the business rules are concerned. The above implementation will return true for two objects with same sso id but with different values for say name or email or both. Should I change my equals contract to check the equality of all fields? What is your suggestion?

Vagrant answered 5/7, 2010 at 17:57 Comment(1)
It really depends on your system's spec. Do you consider two users with different name two separate users? if so, then "ssold must be unique" is not the only requirment.Finial
R
18

This is (almost) correct for "technical equality", but not for "natural equality". To achieve top technical equality, you should also test the reflexive o == this. It may happen that the object isn't persisted in DB yet and thus doesn't have a technical ID yet. E.g.

public class User {

    private Long id;

    @Override
    public boolean equals(Object object) {
        return (object instanceof User) && (id != null) 
             ? id.equals(((User) object).id) 
             : (object == this);
    }

    @Override
    public int hashCode() {
        return (id != null)
             ? (User.class.hashCode() + id.hashCode())
             : super.hashCode();
    }

}

For "natural equality" you should rather compare all non-technical properties. For "real world entities" this is after all more robust (but also more expensive) than technical equality.

public class User {

    private String name;
    private Date birth;
    private int housenumber;
    private long phonenumber;

    @Override
    public boolean equals(Object object) {
        // Basic checks.
        if (object == this) return true;
        if (!(object instanceof User)) return false;

        // Property checks.
        User other = (User) object;
        return Objects.equals(name, other.name)
            && Objects.equals(birth, other.birth)
            && (housenumber == other.housenumber)
            && (phonenumber == other.phonenumber);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, birth, housenumber, phonenumber);
    }

}

True, that's lot of code when there are a lot of properties. A bit decent IDE (Eclipse, Netbeans, etc) can just autogenerate equals(), hashCode() (and also toString(), getters and setters) for you. Take benefit of it. In Eclipse, rightclick code and peek the Source (Alt+Shift+S) menu option.

See also:

Rovelli answered 5/7, 2010 at 18:4 Comment(0)
P
2

If in your model ssoid must be unique, that implies that the values for the other fields should not be different for two instances of User. If you want to validate that assumption, you could do so with assertions within the equals method if the overhead is not an issue.

Plumlee answered 5/7, 2010 at 18:4 Comment(0)
R
2

What you're doing seems fine, and you're not violating any of the rules that equals must follow.

You may still want to check other fields, not to change equals's semantics, but to detect an inconsistency in your business logic, and possibly trigger an assertion/exception.

Ribbentrop answered 5/7, 2010 at 18:6 Comment(2)
Using instanceof will fail the symmetric requirement. Consider B extends A, where a is an A and b is a B. a.equals(b) is not consistent with b.equals(a) if instanceof is used. To be symmetric you have to compare the class (using getClass).Man
@Steve Kuo: If both A.equals() and B.equals() check for "instanceof User, then everything's fine!Stately
S
2

This is a tricky decision to make.

This is a spot i got into when considering hashing a few months ago. I would suggest you read up on what a hash is because it is highly relevant to your answer ... i suggest that you are looking to implement some kind of hash and test its equality.

There are different kinds of equality ... there is the equality of the identity of the object, the equality of the data of the object, the equality of the entire object ... you could also include audit information in there also.

The fact is that 'equal' has many possible meanings.

I resolved this by implementing equal as a strict equality across all fields simply because after asking around it seems to be the intuitive meaning of equals. I then constructed methos for the other kinds of equality i required and defined an interface to wrap these.

I wouldnt test equality on object == this because often you are testing two different objects with the same data which in my book are equal despite them referring to different memory addresses.

Shakhty answered 5/7, 2010 at 18:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.