Search code examples
javacomparisonequalshashcodeapache-commons-lang

Hashcode & equals implementation


I'm writing some code to demonstrate equals and hashcode, for my implementation I used User example :

public class User {
    private String name;
    private String pass;
    //...
    @Override
    public boolean equals(Object obj) {
        if (obj == null) { return false; }
        if (obj == this) { return true; }
        if (obj.getClass() != this.getClass()) {
            return false;
        }
        User rhs = (User) obj;

        EqualsBuilder eb = new EqualsBuilder();
        eb.append(this.getName(), rhs.getName());
        //eb.append(this.getPass(), rhs.getPass());
        return eb.isEquals();
    }
    @Override
    public int hashCode() {
        HashCodeBuilder hcb = new HashCodeBuilder(17, 37);
        hcb.append(this.getName());
        hcb.append(this.getPass());
        return hcb.toHashCode();
    }
    //...
    public static void main(String[] args) {

        User u1 = new User("foo","foo1");
        User u2 = new User("bar","bar1");
        System.out.println(u1.equals(u2));
        System.out.println(u1.hashCode() + " ?= " + u2.hashCode());
        User u3 = new User("foo","foo1");
        User u4 = new User("foo","bar1");
        System.out.println(u3.equals(u4));
        System.out.println(u3.hashCode() + " ?= " + u4.hashCode());

    }
}

Output:

false
2128613651 ?= 2129111967
true
2128615478 ?= 2214545177

I think I'm doing it wrong because my objects now can be equals but have a different hashcode (which is bad I know), but I want to make my Users equals only when their names are also equals.. And not when they have name AND pass equals.

How can I respect the conventions and have what I want to achieve? Thanks for your help/clarification :)


Solution

  • Generally speaking, if a field is not present in your equals() method, you shouldn't reference it in your hashCode() method.

    This ensures your hashcode() result will not change more often that your equals() result does. Consider changing your method to:

    @Override
    public int hashCode() {
        HashCodeBuilder hcb = new HashCodeBuilder(17, 37);
        hcb.append(this.getName());
        return hcb.toHashCode();
    }
    

    That being said, it's rarely a good idea to create a non-intuitive equals() method. I would naturally assume your method would consider the password too, as might many of your future maintainers. Unit tests may be harder to write if true equality testing isn't conducted. Consider whether it would be better to create your own method, e.g.

    public boolean usernameEquals(User other) {
      ...
    }