元に戻したい!
質問:アップキャストしたらサブクラスのメソッドが使えなくなるけど、使いたい時はどうすればいいの?
解答:ダウンキャストしましょう。
アップキャストしてスーパークラスの参照型変数に入れると、その参照型変数を通して「サブクラスにしかないフィールド・メソッド」を使うことはできません。
これは、「7.2 参照型変数の型とインスタンスの型」で説明したように「どのフィールド・メソッドが使えるか」は参照型変数の型で決まるからです。たとえインスタンスの型がサブクラスでも、参照型変数の型がスーパークラスなら、スーパークラスのフィールド・メソッドしか使えません。
ということは、インスタンスを参照する参照型変数がすべてスーパークラスの型だったら、もうサブクラスのフィールド・メソッドは使えないのでしょうか?
そんなことはありません。
そういう時は、ダウンキャストを使って、インスタンスの型の参照型変数に入れ直せばいいんです。
それを実際に試してみましょう。
/**
* 何も持たないクラス。
*/
class NoMemberSuper
{
}
/**
* NoMemberSuperクラスから継承した、サブクラス。
* メソッドを追加しています。
*/
class AddedMethodSub extends NoMemberSuper
{
/**
* 自クラスの名前を出力するメソッドです。
*/
void printMyName()
{
System.out.println( "AddedMethodSub" );
}
}
/**
* 実行用クラス。このクラスを実行してください。
*/
class AddedMethodSubRunner
{
public static void main( String[] args )
{
// インスタンスを1つ作ります。
// ただし、参照型変数はスーパークラスの型にします。
NoMemberSuper refSuper = new AddedMethodSub();
// refを通してprintMyName()メソッドを呼び出すことは
// できません。
// refSuper.printMyName();
// コンパイルエラー:
// AddedMethodSubRunner.java:38: シンボルを見つけられません。
// シンボル: メソッド printMyName()
// 場所 : NoMemberSuper の クラス
// refSuper.printMyName();
// ^
// サブクラスにダウンキャストします。
AddedMethodSub refSub = (AddedMethodSub)refSuper;
// printMyName()メソッドを呼び出します。
refSub.printMyName();
// 出力結果:
// AddedMethodSub
}
}
// AddedMethodSubRunner.java /** * 何も持たないクラス。 */ class NoMemberSuper { } /** * NoMemberSuperクラスから継承した、サブクラス。 * メソッドを追加しています。 */ class AddedMethodSub extends NoMemberSuper { /** * 自クラスの名前を出力するメソッドです。 */ void printMyName() { System.out.println( "AddedMethodSub" ); } } /** * 実行用クラス。このクラスを実行してください。 */ class AddedMethodSubRunner { public static void main( String[] args ) { // インスタンスを1つ作ります。 // ただし、参照型変数はスーパークラスの型にします。 NoMemberSuper refSuper = new AddedMethodSub(); // refを通してprintMyName()メソッドを呼び出すことは // できません。 // refSuper.printMyName(); // コンパイルエラー: // AddedMethodSubRunner.java:38: シンボルを見つけられません。 // シンボル: メソッド printMyName() // 場所 : NoMemberSuper の クラス // refSuper.printMyName(); // ^ // サブクラスにダウンキャストします。 AddedMethodSub refSub = (AddedMethodSub)refSuper; // printMyName()メソッドを呼び出します。 refSub.printMyName(); // 出力結果: // AddedMethodSub } }
このプログラムには、何も持たないNoMemberSuperクラスと、そのクラスから継承してメソッドを追加したAddedMethodSubクラスがあります。
* 何も持たないクラス。
*/
class NoMemberSuper
{
}
/**
* NoMemberSuperクラスから継承した、サブクラス。
* メソッドを追加しています。
*/
class AddedMethodSub extends NoMemberSuper
{
/**
* 自クラスの名前を出力するメソッドです。
*/
void printMyName()
{
System.out.println( "AddedMethodSub" );
}
}
/** * 何も持たないクラス。 */ class NoMemberSuper { } /** * NoMemberSuperクラスから継承した、サブクラス。 * メソッドを追加しています。 */ class AddedMethodSub extends NoMemberSuper { /** * 自クラスの名前を出力するメソッドです。 */ void printMyName() { System.out.println( "AddedMethodSub" ); } }
この2つのクラスは継承関係にあるので、アップキャストしてスーパークラスの参照型変数に参照を入れることができます。
// ただし、参照型変数はスーパークラスの型にします。
NoMemberSuper refSuper = new AddedMethodSub();
// インスタンスを1つ作り数値を入れます。 // ただし、参照型変数はスーパークラスの型にします。 NoMemberSuper refSuper = new AddedMethodSub();
このように、インスタンスを作った際にアップキャストすると、インスタンスを参照するのはrefSuper変数だけになって、このままだとAddedMethodSubクラスが持つprintMyName()メソッドを呼び出すことができません。
このように、スーパークラスの参照型変数でしか参照していない状態から、サブクラスのフィールド・メソッドを使えるようにするためには「ダウンキャスト」ということを行います。
ダウンキャストとは、スーパークラスからサブクラスへとキャストすることです。
AddedMethodSub refSub = (AddedMethodSub)refSuper;
// サブクラスにダウンキャストします。 AddedMethodSub refSub = (AddedMethodSub)refSuper;
refSuper変数の型は「スーパークラス」です。
その変数の前に「(AddedMethodSub)」を付けることで、AddedMethodSubクラス、つまりサブクラスにキャストすることができます。
この「サブクラスへのキャスト」を「ダウンキャスト」と言います。
ダウンキャストすることで、サブクラスの参照型変数であるrefSub変数に参照を入れることができます。
そうすれば、晴れてインスタンスが持っているprintMyName()メソッドを使うことができる、というわけです。
refSub.printMyName();
// 出力結果:
// AddedMethodSub
// printMyName()メソッドを呼び出します。 refSub.printMyName(); // 出力結果: // AddedMethodSub
このように、ダウンキャストを使うことで、一度アップキャストしたものを、元のインスタンスの型に戻すことができるというわけです。
ここで重要なことは、ダウンキャストしてもインスタンスの型は変わらないという点です。
アップキャストした時もそうでしたが、アップキャスト・ダウンキャストをしても、参照型変数の型が変わるだけで、インスタンスは何にも変化しません。この点をしっかり理解しておいてください。
ちなみにアップキャストとダウンキャストをまとめると、以下のようになります。
アップキャスト:サブクラス→スーパークラス:自動的にできる。
ダウンキャスト:スーパークラス→サブクラス:「(サブクラス)」が必要。
クラスの関係を書くときには通常「上にスーパークラス」「下にサブクラス」を書くので、上方向を「アップキャスト」、下方向を「ダウンキャスト」と言います。