MENU

【Java】オーバーロード・オーバーライドまとめ【Java Silver】

こんにちは。くまごろーです。
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.オーバーライドとオーバーロード)