Javaのメソッドオーバーライドに関するメモ。基本的なことなんだけど、すぐ忘れちゃうので。

■オーバーライドとオーバーロードって、どう違うの?

  • 親クラスのメソッドを同じシグニチャで上書き定義するのがオーバーライド(override)。同じメソッド名でいろんな型の引数を定義するのがオーバーロード(overload)。

■privateなメソッドって、子クラスでオーバーライドできるの?

  • できません。親と同じシグニチャのメソッドを書いても、それで上書きはできません。例えば、下記を実行すると、BではなくAと出力されます。
class A {
    private void privateMethod() {
        System.out.println("A");
    }
    public void invokePrivateMethod() {
        privateMethod();
    }
}
class B extends A {
    private void privateMethod() {
        System.out.println("B");
    }
}
public class Main {
    public static void main(String[] args) {
        new B().invokePrivateMethod();
    }
}

■オーバライドするときに、子クラスで可視性を変えることはできるの?

  • 可視性を狭めるのはコンパイルエラーになります(「○○より弱いアクセス権限を割り当てようとしました」)。可視性を広げる方向に変えるのはOK。

    • defaultからprivateに変えるのは×、protectedかpublicに変えるのは○
    • protectedからprivateかdefaultに変えるのは×、publicに変えるのは○
    • publicからpriveteかdefaultかprotectedに変えるのは×

■finalなメソッドって、子クラスでオーバーライドできるの?

  • できません。コンパイルエラーになります(「オーバーライドされたメソッドはfinalです」)。

■staticなメソッド(クラスメソッド)って、子クラスでオーバーライドできるの?

  • 同じシグニチャのメソッドは書けますが、それはオーバーライドじゃなくて、メソッドの「隠蔽」。クラスメソッドってのはクラスを指定して呼び出すものだから、Parent.foo()で親のfoo()が呼ばれるし、Child.foo()で子のfoo()が呼ばれます。

■オーバライドするときに、子クラスで戻り値の型を変えることはできる?

  • 基本的にはできません。コンパイルエラーになります(「戻り値の型FooはBarと互換性がありません」)。ただし、1.5以降は、元の型のサブクラスに変えるのはOKになっています。つまり、型をより具体的なものに限定する方向はOK。ちなみに、このように型を狭い方向に変えることを共変(covariant)といい、逆に広い方向に変えることを反変(contravariant)といいます。

■オーバーライドするときに、子クラスでthrows宣言する例外を変えることはできるの?

  • 例外を増やすとコンパイルエラーになります(「オーバーライドされたメソッドは○○をスローしません」)。例外を減らすのはOK、つまり例外をより限定する方向はOKです。なので、例外を元の型のサブクラスに変えるのもOK。ちなみに、RuntimeExceptionならthrowsに追加してもコンパイルエラーにはなりませんが、もともとthrowsに書かなくても投げられるのがRuntimeExceptionなわけで、そんなことしても意味はないでしょう。

■オーバーライドするときに、子クラスで型パラメータを変えることはできるの?

  • できません。コンパイルエラーになります。たとえば、下記をコンパイルすると……
class A {
    public <T> void foo() {}
}
class B extends A {
    public <T extends Number> void foo() {}
}

このようなエラーになります。

XXX.java:5: エラー: 名前が競合しています。Bの<T#1>foo()とAの<T#2>foo()はまだ他方をオーバーライドしていませんが、削除後の名前が同じです
class B extends A {
^
T#1,T#2が型変数の場合:
    メソッド <T#1>foo()で宣言されているT#1 extends Number
    メソッド <T#2>foo()で宣言されているT#2 extends Object
エラー1個

※バージョンメモ

  • JavaSE 1.7