アップキャストとダウンキャスト
CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 30/87。
前のレッスンでは、親型の変数が子オブジェクトを保持できることを見ました:Animal myPet = new Dog()。この代入はアップキャスティングと呼ばれ、Dogは常にAnimalであるため、自動的に行われます。
アップキャストは継承階層を上に移動します。これは暗黙的であり、常に安全です:
Dog dog = new Dog();
Animal animal = dog; // アップキャスト - 自動的アップキャストすると、親クラスで定義されたメソッドにのみアクセスできるようになります。オブジェクトの実体は依然として Dog ですが、子クラス固有のメソッドは隠されて見えなくなります。
ダウンキャストは逆方向の操作で、親の参照を子の型に変換します。これは常に安全であるとは限らないため、明示的なキャストが必要です:
Animal animal = new Dog(); // アップキャスト
Dog dog = (Dog) animal; // ダウンキャスト - 明示的なキャストが必要
dog.bark(); // これでDogのメソッドにアクセスできるようになりますダウンキャストは危険です。もしオブジェクトが実際にキャスト先の型ではない場合、Javaは実行時に ClassCastException をスローします:
Animal animal = new Cat();
Dog dog = (Dog) animal; // コンパイルは可能ですが、実行時にクラッシュします!コンパイラは宣言された型しか認識しないため、このエラーをキャッチすることはできません。実際のオブジェクト型は、プログラムの実行時にのみ判明します。次のレッスンでは、ダウンキャストを行う前にオブジェクトの型を安全にチェックする方法を学びます。
チャレンジ
簡単アップキャスティングとダウンキャスティングの両方の動作を実証するメディアプレーヤーシステムを構築しましょう。メディアタイプの階層を作成し、親リファレンスと子リファレンスの間の変換を練習します。
コードは4つのファイルに分けて構成します:
Media.java: すべてのメディアタイプが共有するベースクラスを作成します。すべてのメディアアイテムはtitleフィールド(String)を持ちます。これを初期化するためのコンストラクタ、ゲッターメソッドgetTitle()、そして"Playing: " + titleと出力するplay()メソッドを含めてください。Song.java: Mediaを継承するクラスを作成します。Songには追加のartistフィールド(String)があります。親の初期化にはsuperを使用してください。play()をオーバーライドして、"Playing song: " + title + " by " + artistと出力するようにします。また、"Artist: " + artistと出力するshowArtist()メソッドを追加してください。Podcast.java: Mediaを継承する別のクラスを作成します。Podcastにはepisodeフィールド(int)があります。play()をオーバーライドして、"Playing podcast: " + title + " - Episode " + episodeと出力するようにします。また、"Episode: " + episodeと出力するshowEpisode()メソッドを追加してください。Main.java: メディアタイプを使用してアップキャスティングとダウンキャスティングを実証します。4つの入力(曲のタイトル、アーティスト名、ポッドキャストのタイトル、エピソード番号)を受け取ります。まず、Songを作成し、それをMediaリファレンスにアップキャストします。このリファレンスで
play()を呼び出し、実行時のポリモーフィズムが機能することを確認してください。次に、そのMediaリファレンスをSongにダウンキャストし、子クラス固有のメソッドにアクセスするために
showArtist()を呼び出します。その後、Podcastを作成し、それをMediaリファレンスにアップキャストします。このリファレンスで
play()を呼び出します。最後に、そのMediaリファレンスをPodcastにダウンキャストし、
showEpisode()を呼び出します。
4つの入力(曲のタイトル (String)、アーティスト名 (String)、ポッドキャストのタイトル (String)、エピソード番号 (int))を受け取ります。
出力は4行にする必要があります。2行は曲に関するもの(再生出力とアーティスト情報)、もう2行はポッドキャストに関するもの(再生出力とエピソード情報)です。アップキャスティングによって異なるメディアタイプを統一的に扱える一方で、ダウンキャスティングによってそれぞれの固有の機能にアクセスできることに注目してください!
チートシート
Javaにおいて、アップキャストは継承階層を上に移動し、自動的に行われます。子オブジェクトは親型の変数に代入できます:
Dog dog = new Dog();
Animal animal = dog; // アップキャスト - 自動的アップキャストすると、オブジェクトの実際の中身は依然として子型であっても、親クラスで定義されたメソッドにしかアクセスできなくなります。
ダウンキャストは、親の参照を子型に戻します。これには明示的なキャストが必要です:
Animal animal = new Dog(); // アップキャスト
Dog dog = (Dog) animal; // ダウンキャスト - 明示的なキャストが必要
dog.bark(); // これでDogのメソッドにアクセスできるようになりますダウンキャストは危険です。オブジェクトが実際にキャスト先の型ではない場合、Javaは実行時にClassCastExceptionをスローします:
Animal animal = new Cat();
Dog dog = (Dog) animal; // コンパイルは通りますが、実行時にクラッシュします!コンパイラは宣言された型しか見ていないため、このエラーをキャッチできません。実際のオブジェクト型は、プログラムの実行時にのみ判明します。
自分で試してみよう
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 入力を読み込む
String songTitle = scanner.nextLine();
String artistName = scanner.nextLine();
String podcastTitle = scanner.nextLine();
int episodeNumber = scanner.nextInt();
// TODO: Songを作成し、Mediaリファレンスにアップキャストする
// TODO: Mediaリファレンスでplay()を呼び出す(ポリモーフィズムのデモンストレーション)
// TODO: MediaリファレンスをSongにダウンキャストする
// TODO: SongリファレンスでshowArtist()を呼び出す
// TODO: Podcastを作成し、Mediaリファレンスにアップキャストする
// TODO: Mediaリファレンスでplay()を呼び出す
// TODO: MediaリファレンスをPodcastにダウンキャストする
// TODO: PodcastリファレンスでshowEpisode()を呼び出す
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4継承
継承の基本 (extends)super キーワードメソッドのオーバーライド (@Override)コンストラクタチェーンObject クラス単一継承と多段階継承なぜ多重継承ができないのかまとめ:従業員の階層構造