こんにちは。くまごろーです。
Java Silverの勉強をしていたら、オーバーロード・オーバーライドがよくわからなくなってきました。
最初わかったつもりでいたんですが、インターフェースや抽象クラスが絡んできたら途端に頭が混乱してきてしまって(-"-)
というわけで、今日はメソッドのオーバーロード・オーバーライドについてまとめます。
1 オーバーロード/オーバーライドとは
(1)オーバーロード
「メソッドの多重定義」とも呼ばれる、同名のメソッドを複数宣言できる機能。
<オーバーロードの条件>
・名前が同じ
・引数の数や型、順番が異なる
※ 戻り値型やアクセス修飾子は関係ない
<例>
(メソッドx)
int sample(double a, int b) { //do something }
(メソッドxのオーバーロード1)
int sample(double a, double b) { //引数が異なる //do something }
(オーバーロード2)
double sample(double a) { //戻り値等は関係ない //do something }
(2)オーバーライド
オーバーライドとは、サブクラスでスーパークラスに定義されたメソッドを「再定義」すること。
<オーバーライドの条件>
・シグニチャ(メソッド名と引数のセット)が同じである
・戻り値は同じ型か、そのサブクラス型である
・アクセス修飾子は同じか、より緩いものである。
<例>
(スーパークラス)
class SuperClass { void methodA(){ //do something } }
(サブクラス)
class SubClass extends SuperClass{ void methodA(){ //シグニチャ・戻り値が同じ //do something } }
2 色んなパターン
(1)オーバーロードとオーバーライド・共変戻り値が絡むケース
(スーパークラス)
import java.util.Collection; public class A { public void sample(Collection arg) { System.out.println("A"); } }
(サブクラス)
import java.util.Collection; import java.util.List; public class B extends A { public void sample(Collection arg) { //オーバーライド System.out.println("B"); } public void sample(List arg) { //オーバーロード System.out.println("C"); //引数の「List」はCollectionのサブインターフェース } }
(Mainクラス)
/*省略*/ A a1 = new A(); A a2 = new B(); B b1 = new B(); List<String> list = new ArrayList<>(); a1.sample(list); //A a2.sample(list); //B(※) b1.sample(list); //C
※ サブクラス(B)のインスタンスをスーパークラス(A)の型で扱った時、JVMはインスタンスにオーバーライドしたメソッドがあればそのメソッドを、なければスーパークラスのメソッドを実行する。
※ CollectionはListインターフェースのスーパーインターフェースであり、List型の引数ならCollection型を受け取るメソッドにも適用できる。(Collection型とList型、どちらのメソッドもある場合は、厳密な型(List)が選択される。)このように、メソッドをオーバーライドした際に戻り値型をサブクラスやサブインターフェースに出来る機能を「共変戻り値」という。
(2)菱形継承問題とオーバーライド
(インターフェースA)
public interface A { void sample(); }
(インターフェースB)
public interface B extends A { default void sample() { //Cとデフォルトメソッドが重複 System.out.println("B"); } }
(インターフェースC)
public interface C extends A { default void sample() { //Bとデフォルトメソッドが重複 System.out.println("C"); } }
(クラスD)
public class D implements B, C { @Override public void sample() { B.super.sample(); //BとCどちらの実装を使うか指定する } }
※ インターフェースB,Cは、インターフェースAを継承しており、クラスDはインターフェースB,Cを実装している。
このように、インターフェースの多重実現をした結果、デフォルトメソッドが重複することを「菱形継承問題」という。この場合、Dクラスでデフォルトメソッドをオーバーライドし、その際にBとCどちらのデフォルトメソッドメソッドを呼び出すか指定しないと、コンパイルエラーになる。
3 参考
Javaの道:クラス(9.オーバーライドとオーバーロード)