Javaのオーバーライドに関する素朴な疑問集
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