Write simpler equals & hashCode Java methods using EqualsBuilder & HashCodeBuilder from Apache Commons

Last week I posted about how to implement a Hibernate-safe equals() method using instanceof and accessors (getters) in Eclipse. This showed that you don't want to be comparing the member variables directly but should use the accessor methods to protect against Hibernate's use of proxy objects.

My old colleague Guy Francis pointed out that rather than using Eclipse's generated (lengthy and rather ugly) equals and hashCode methods I should take a look at the Apache Commons EqualsBuilder and HashCodeBuilder.

Both of these builders have a very useful reflection-based equals/hashcode method but that does a deep internal comparison of member variables so that is no good for use with Hibernate objects. One use that they don't really explain clearly (and hence the reason for this blog post) is whether you can use accessor (getter) methods instead of direct variable access when using these builders...

...well you can!

You create a new EqualsBuilder object inside your equals method, append access to any properties of your object that make it unique and then call the isEquals method to test the properties. You do a very similar thing for hashCode. This is my new version of last week's methods now using these builders rather than Eclipse's generated code:

@Override
public boolean equals(Object obj) {
if (obj instanceof Person) {
Person other = (Person) obj;
EqualsBuilder builder = new EqualsBuilder();
builder.append(getId(), other.getId());
builder.append(getName(), other.getName());
return builder.isEquals();
}
return false;
}

@Override
public int hashCode() {
HashCodeBuilder builder = new HashCodeBuilder();
builder.append(getId());
builder.append(getName());
return builder.toHashCode();
}
They are not only smaller and simpler, they are also much less error-prone and it's very clear which object properties are used to see if an object is 'equal'. I'm going to be using these builders from now on for all my equals and hashCode methods!

Technorati Tags: , , , , ,

18 comments:

Nhoj Yelruc said...

Andy,

Very helpful blog on the Apache Commons equals() and hashCode() helper classes.

I will add them to my bag of tricks. Thanks.

You rock from across the pond!

Rouy Dneirf,
Nhoj

Anonymous said...

Helped me very much, thanks.

Anonymous said...

This is why it is not a good idea to use Java as a teaching language: it leads naïve people to believe that the above is in any way a solution you would ever want to see in code you actually care about.

abeacock said...

Anonymous, are you referring to the original handwritten one or the Builder version?

Anonymous said...

You shouldn't use instanceof in the equals method. You can end up with a Cat being equal to a Dog because they're equal at the Mammal level (2 eyes, tail, fur).

if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final Foo other = (Foo) obj;
...

abeacock said...

Anon, thanks for your comment, it's a very very good point and one that I thought I had overlooked.

One area where it is useful though is when you are comparing Hibernate objects the getClass for your Hibernate objects returns a proxy to the real one the getClass won't return true.

Other than that it's a great idea.

Will said...

I think there is a bug in the hashCode method. You should be using HashCodeBuilder#toHashCode(), not HashCodeBuilder#hashCode().

A concise way to do it is:

return new HashCodeBuilder()
.append(getId())
.append(getName())
.toHashCode();

abeacock said...

Cheers Will, all updated now!

abeacock said...

Will, the reason for me not using the cascade calling style (which is great BTW) is that our Eclipse setup makes a mess of the formatting and tries to put it all on one line...

hari babu said...

NIce post, good information about java methods , usefull article

atoms said...

Nice post it really help mep

Ravi Venkatesan said...

Very useful. Here is a utility class to generate the equals and hashcode methods.
http://ravivenkatesan.blogspot.com/

Anonymous said...

Using the apache equals and hascode builders is for code that doensn't care about performance, Using theese builders you alocate objects every time you call equals and hashcode complicating the job of the GC, these "helpers" will contribute to increase your memory usage and worse performance with no real benefit!
I advice against using theese helpers, and recomend using the IDE generated implementations.

Will said...

The builders do have the potential for slower performance. However, in modern JVM's it is possible that the overhead will be optimized away (even the object allocation). This is a good article about it http://www.ibm.com/developerworks/java/library/j-jtp09275.html

IDE generated methods are a good way to to too. This is somewhat of a stylistic decision where you should weigh the readability, maintainability, 3rd party library overhead, and performance. But as JVM's get better, the performance impact becomes negligible.

Javin @ FIX Protocol Tutorial said...

Nice article but I believe its important to understand the consequences of not following this contract as well and for that its important to understand application of hashcode in collection classes e.g. How HashMap works in Java and how hashcode() of key is used to insert and retrieve object from hashMap.

Javin

pranav said...

Very helpful article , as I am exploring the Apache common utilities.

Anonymous said...

I was under impression that IDE are simple and easy to use to generate hashcode method in Java, specially Netbeans but this looks better, though it has dependency to add Apache commons on your build path.

Andy B said...

The additional dependancy is a good point but I find that the Apache Commons packages have so much value that their inclusion means that I write less code and end up with more stable code. They have so many helper classes that you barely have to write any mundane transforming or search code at all.