Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Right way to implement equals contract [duplicate]

Tags:

java

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?

like image 621
chedine Avatar asked Jul 05 '10 17:07

chedine


4 Answers

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:

  • JBoss: Equals and HashCode (in view of persistence)
  • Hibernate: Persistent Classes - implementing equals() and hashCode()
  • Related SO question: Overriding equals and hashCode in Java
like image 151
BalusC Avatar answered Oct 26 '22 19:10

BalusC


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.

like image 28
btreat Avatar answered Oct 26 '22 19:10

btreat


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.

like image 33
casablanca Avatar answered Oct 26 '22 18:10

casablanca


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.

like image 32
John Nicholas Avatar answered Oct 26 '22 19:10

John Nicholas