40.一貫してOverrideアノテーションを使うべし
Javaライブラリはいくつかのアノテーションを提供してるが、多くのプログラマにとって、@Override
が一番重要だろう。
一貫して@Override
を使用していれば、バグが起こりにくくなる。
以下のbigramを模したクラスを例に見ていく(バグあり)。
package tryAny.effectiveJava;
import java.util.HashSet;
import java.util.Set;
//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<>();
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());
}
}
このプログラムは26を出力すると思いきや、260を出力する。
原因はequalsをOverrideするつもりがOverloadしてしまっていることにある。Object.equals
の引数はObject型であり、シグニチャが違う。
こういった誤りを防ぐために、Overrideするメソッドには@Override
をつける。そうすると以下のコードはコンパイルエラーとなる。
@Override public boolean equals(Bigram b) {
return b.first == first && b.second == second;
}
正しくは以下のようになる。
@Override
public boolean equals(Object o) {
if (!(o instanceof Bigram))
return false;
Bigram b = (Bigram) o;
return b.first == first && b.second == second;
}
このようなことから、親クラスのメソッドをOverrideするときは@Overrideをつけるべきである。
これには一つ例外があって、具象クラスにおいて、親の抽象メソッドをOverrideするときは、そもそもOverrideしないとコンパイルエラーが出るので、@Override
の記述の必要はない(記述による害もない)。