Saturday 7 July 2012

Item 36: Consistently use the Override annotation


When annotations were added to the language in release 1.5, several annotation types were added to the libraries [JLS, 9.6.1]. For the typical programmer, the most important of these is Override. This annotation can be used only on method declarations, and it indicates that the annotated method declaration overrides a declaration in a supertype. If you consistently use this annotation, it will protect you from a large class of nefarious bugs. Consider this program, in which the class Bigram represents a bigram, or ordered pair of letters:

// Can you spot the bug?
public class Bigram {
private final char first;
private final char second;
public Bigram(char first, char second) {
this.first = first;
this.second = second;
}
public boolean equals(Bigram b) {
return b.first == first && b.second == second;
}
public int hashCode() {
return 31 * first + second;
}
public static void main(String[] args) {
Set<Bigram> s = new HashSet<Bigram>();
for (int i = 0; i < 10; i++)
for (char ch = 'a'; ch <= 'z'; ch++)
s.add(new Bigram(ch, ch));
System.out.println(s.size());
}
}

Clearly, the author of the Bigram class intended to override the equals method (Item 8) and even remembered to override hashCode in tandem (Item 9). Unfortunately, our hapless programmer failed to override equals, overloading it instead (Item 41). To override Object.equals, you must define an equals method whose parameter is of type Object.

Luckily, the compiler can help you find this error, but only if you help the compiler by telling it that you intend to override Object.equals. To do this, annotate Bigram.equals with @Override, as shown below:

@Override public boolean equals(Bigram b) {
return b.first == first && b.second == second;
}

If you insert this annotation and try to recompile the program, the compiler will generate an error message like this:

Bigram.java:10: method does not override or implement a method
from a supertype
@Override public boolean equals(Bigram b) {
^

You will immediately realize what you did wrong, slap yourself on the forehead, and replace the broken equals implementation with a correct one (Item 8):

@Override public boolean equals(Object o) {
if (!(o instanceof Bigram))
return false;
Bigram b = (Bigram) o;
return b.first == first && b.second == second;
}

Therefore, you should use the Override annotation on every method declaration that you believe to override a superclass declaration. There is one minor exception to this rule. If you are writing a class that is not labeled abstract, and you believe that it overrides an abstract method, you needn’t bother putting the Override annotation on that method. In a class that is not declared abstract, the compiler will emit an error message if you fail to override an abstract superclass method.

Modern IDEs provide another reason to use the Override annotation consistently. Such IDEs have automated checks known as code inspections. If you enable the appropriate code inspection, the IDE will generate a warning if you have a method that doesn’t have an Override annotation but does override a superclass method.

If you are using release 1.6 or a later release, the Override annotation provides even more help in finding bugs. In release 1.6, it became legal to use the Override annotation on method declarations that override declarations from interfaces as well as classes. In a concrete class that is declared to implement an interface, you needn’t annotate methods that you believe to override interface methods, because the compiler will emit an error message if your class fails to implement every interface method. Again, you may choose to include these annotations simply to draw attention to interface methods, but it isn’t strictly necessary.

For example, the Set interface adds no new methods to the Collection interface, so it should include Override annotations on all of its method declarations, to ensure that it does not accidentally add any new methods to the Collection interface.

In summary, the compiler can protect you from a great many errors if you use the Override annotation on every method declaration that you believe to override a supertype declaration, with one exception. In concrete classes, you need not annotate methods that you believe to override abstract method declarations (though it is not harmful to do so).


Reference: Effective Java 2nd Edition by Joshua Bloch