Friday, February 1, 2008

Annotation @Override: Making the compiler work for you

I was writing/running JUnit tests for a project at work today when I came accross some odd behavior with the assertEquals method. Before I go into the problem, let me over simplify the model I was working with.

I created a class Foo that extended a class Boo, written by another project team. Boo overrode the Object.equals method (or so I thought), as shown in the diagram below.

Now from the simple diagram, coupled with the fact that you know somethings up, you may have guessed that they didn't really override the equals method in Boo. They overloaded it by changing the signature from

boolean equals(Object boo)

to

boolean equals(Boo boo)

This was a mistake on their part, an oversight on mine, and the result was the following statement in my JUnit test failed

assertEquals( fooA, fooB );

Whereas the following statements passed without a problem.

assertTrue( fooA.equals( fooB ) );

assertTrue( fooB.equals( fooA ) );

It took me a while to take notice of the signature mismatch on Boo.equals. (no jokes or insults please :) ).

It passed on assertTrue because assertTrue invoked fooA.equals( fooB ), and when the JVM didn't find an exact signature match on the method in Foo, or a method of equal name but more general types, it went to the next closest match, which was the equals method of Boo taking a Boo for comparison.

It failed on assertEquals because assertEquals treated both objects passed in as java.lang.Objects. This meant that the equals method of Object, unless overridden, was going to be called. Since it wasn't overridden, the result was the equivelant of fooA == fooB.

My Point

My point is that, if they had put an annotation tag @Override on their equals method, Boo wouldn't have compiled in the first place and I wouldn't have gone through this. And I thought it was worth brining attention to on my blog as I haven't seen many people use it yet since made available in Java 1.5.