メソッドオーバーライド(実行時)
CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 29/87。
メソッドのオーバーロードはコンパイル時に解決されますが、メソッドのオーバーライドは実行時ポリモーフィズムを示します。ここでは、Javaはプログラムが実際に実行されているときに、変数の宣言された型ではなく、オブジェクトの実際の型に基づいて、どのメソッドを実行するかを決定します。
継承の章でメソッドのオーバーライドについて学びました。これがポリモーフィック(多態的)である理由は、親クラスの参照が子クラスのオブジェクトを指しているときに、Javaがそれをどのように処理するかにあります。
public class Animal {
public void speak() {
System.out.println("Some sound");
}
}
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Bark!");
}
}
public class Cat extends Animal {
@Override
public void speak() {
System.out.println("Meow!");
}
}親型の参照を使用するとどうなるか見てみましょう:
Animal myPet = new Dog();
myPet.speak(); // 出力: Bark!
myPet = new Cat();
myPet.speak(); // 出力: Meow!myPet が Animal として宣言されていても、Javaは実行時に実際のオブジェクト型に基づいてオーバーライドされたメソッドを呼び出します。これは 動的メソッドディスパッチ と呼ばれます。JVMは変数が何であるかではなく、オブジェクトが実際に何であるかを確認します。
これにより、異なるオブジェクトを均一に処理するような強力なパターンが可能になります:
Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
pet.speak(); // それぞれが自身のバージョンを呼び出します
}実行時ポリモーフィズムを使用すると、親型を扱いながら、自動的に正しい子の振る舞いを使用する柔軟なコードを記述できます。
チャレンジ
簡単実行時ポリモーフィズム(多態性)の動作を実証する通知システムを構築しましょう。通知タイプの継承階層を作成し、実行時の実際のオブジェクトタイプに応じて、同じメソッド呼び出しが異なる出力を生成するようにします。
コードは4つのファイルに分けて構成します:
Notification.java: すべての通知が共有するベースクラスを作成します。すべての通知はrecipientフィールド(String)を持ちます。これを初期化するためのコンストラクタ、ゲッターメソッド、および"Sending notification to " + recipientと出力するsend()メソッドを含めてください。EmailNotification.java: Notification を継承するクラスを作成します。メール通知にはsubjectフィールドを追加します。親クラスの初期化にはsuperを使用してください。send()メソッドをオーバーライドして、"Emailing " + getRecipient() + ": " + subjectと出力するようにします。SMSNotification.java: Notification を継承する別のクラスを作成します。SMS通知にはphoneNumberフィールドを追加します。send()をオーバーライドして、"Texting " + phoneNumber + " for " + getRecipient()と出力するようにします。Main.java: ここで実行時ポリモーフィズムが真価を発揮します!受信者名、メールの件名、電話番号の3つの入力を受け取ります。Notification参照の配列を作成し、次の順序で3つのオブジェクトを格納します:ベースの Notification、EmailNotification、および SMSNotification(すべて同じ受信者を使用します)。次に、配列をループで回し、各要素に対してsend()を呼び出します。Javaが各オブジェクトの実際の内容に基づいて、正しいオーバーライドされたバージョンを自動的に呼び出す様子を確認してください!
入力として、受信者名(String)、メールの件名(String)、電話番号(String)の3つを受け取ります。
出力には3行表示されるはずです。Notification 参照に対して同じメソッドを呼び出しているにもかかわらず、各 send() 呼び出しは異なる出力を生成します。これが動的メソッドディスパッチの仕組みです!
チートシート
メソッドのオーバーライドは、実行時ポリモーフィズム(多態性)を示しています。これは、Javaがプログラムの実行時に、変数の宣言された型ではなく、オブジェクトの実際の型に基づいて、どのメソッドを実行するかを決定する仕組みです。
親クラスの参照が子クラスのオブジェクトを指している場合、Javaは実行時に実際のオブジェクト型に基づいてオーバーライドされたメソッドを呼び出します:
public class Animal {
public void speak() {
System.out.println("Some sound");
}
}
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Bark!");
}
}
Animal myPet = new Dog();
myPet.speak(); // 出力: Bark!
myPet = new Cat();
myPet.speak(); // 出力: Meow!これは動的メソッドディスパッチと呼ばれます。JVMは変数の型ではなく、オブジェクトが実際に何であるかを確認します。
実行時ポリモーフィズムにより、異なるオブジェクトを統一的に処理することが可能になります:
Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
pet.speak(); // それぞれが自身のバージョンを呼び出す
}自分で試してみよう
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 入力を読み込む
String recipient = scanner.nextLine();
String subject = scanner.nextLine();
String phoneNumber = scanner.nextLine();
// TODO: 3つの要素を持つ Notification 参照の配列を作成する
// 配列には(次の順序で)含める必要があります:
// 1. 基本の Notification オブジェクト
// 2. EmailNotification オブジェクト
// 3. SMSNotification オブジェクト
// すべて同じ recipient を使用すること
// TODO: 配列をループで回し、各要素に対して send() を呼び出す
// これは実行時ポリモーフィズムを示しています!
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4継承
継承の基本 (extends)super キーワードメソッドのオーバーライド (@Override)コンストラクタチェーンObject クラス単一継承と多段階継承なぜ多重継承ができないのかまとめ:従業員の階層構造