Javaのキャスト問題は実は複雑ではありません。一言だけ覚えておけばいいのです:親クラスの参照は子クラスのオブジェクトを指す。

親クラスの参照が子クラスのオブジェクトを指すとはどういうことか、ゆっくり説明します。

2つの用語から始めましょう:アップキャスト (upcasting) 、ダウンキャスト (downcasting)。

例を挙げます:2つのクラスがあり、Father は親クラス、Son クラスは Father を継承します。

最初の例:

Father f1 = new Son();   // 这就叫 upcasting (向上转型)
// 现在 f1 引用指向一个Son对象

Son s1 = (Son)f1;   // 这就叫 downcasting (向下转型)
// 现在f1 还是指向 Son对象

2番目の例:

Father f2 = new Father();
Son s2 = (Son)f2;       // 出错,子类引用不能指向父类对象

こう疑問に思うかもしれません。1番目の例では:Son s1 = (Son)f1;なぜ正しいのか、と。

とても簡単です。f1 は子クラスのオブジェクトを指しているからです。Father f1 = new Son();子クラスの参照 s1 は当然子クラスのオブジェクトを指せます。

一方、f2 には Father オブジェクトが渡されています。Father f2 = new Father();子クラスの参照 s2 は親クラスのオブジェクトを指せません。

まとめ:

1、親クラスの参照は子クラスのオブジェクトを指せるが、子クラスの参照は親クラスのオブジェクトを指せない。

2、子クラスのオブジェクトをそのまま親クラスの参照に代入することをアップキャスト(上方キャスト)と呼びます。アップキャストは強制変換を必要としません。例:

Father f1 = new Son();

3、子クラスのオブジェクトを指す親クラスの参照を子クラスの参照に代入することをダウンキャスト(下方キャスト)と呼び、強制変換が必要です。例:

f1 は子クラスのオブジェクトを指す親クラスの参照です。f1 を子クラスの参照 s1 に代入する、すなわちSon s1 = (Son)f1;

その際、f1 の前の (Son) を必ず付けて強制変換を行います。

一、アップキャスト。

平たく言うと、子クラスのオブジェクトを親クラスのオブジェクトに変換することです。ここで親クラスのオブジェクトはインターフェースでも構いません。

1、アップキャストにおけるメソッド呼び出し:

例

public class Animal { public void eat(){ System.out.println("animal eatting..."); } } class Bird extends Animal{ public void eat(){ System.out.println("bird eatting..."); } public void fly(){ System.out.println("bird flying..."); } } class Main{ public static void main(String[] args) { Animal b=new Bird(); //アップキャスト b.eat(); //! error: b.fly(); b は子クラスのオブジェクトを指していますが、このとき fly() メソッドを失っています。 dosleep(new Male()); dosleep(new Female()); } public static void dosleep(Human h) { h.sleep(); } }

例

public class Human { public void sleep() { System.out.println("Human sleep.."); } } class Male extends Human { @Override public void sleep() { System.out.println("Male sleep.."); } } class Female extends Human { @Override public void sleep() { System.out.println("Female sleep.."); } }

ここでのアップキャストに注意してください:

Animal b=new Bird(); //向上转型
b.eat();

ここでは子クラスの eat() メソッドが呼び出されます。理由:b は実際には Bird 子クラスを指しているため、呼び出し時に子クラス自身のメソッドが呼び出されます。

注意すべき点は、アップキャスト時、b は親クラスのオブジェクトと共通のメソッド以外を失うことです。この例の fly メソッドはもはや b のものではありません。

2、アップキャストの利点

上記のコードを見てください:

public static void dosleep(Human h) {
    h.sleep();
}

ここでは親クラスをパラメータとし、呼び出し時に子クラスをパラメータとして使うのは、アップキャストを利用したものです。これによりコードが簡潔になります。そうでなければ、dosleep が子クラスのオブジェクトをパラメータとする場合、子クラスの数だけ関数を書く必要があります。これも JAVA の抽象プログラミング思想を示しています。

二、ダウンキャスト。

アップキャストの逆で、親クラスのオブジェクトを子クラスのオブジェクトに変換することです。

例

package com.wensefu.other1; public class Girl { public void smile(){ System.out.println("girl smile()..."); } } class MMGirl extends Girl{ @Override public void smile() { System.out.println("MMirl smile sounds sweet..."); } public void c(){ System.out.println("MMirl c()..."); } } class Main{ public static void main(String[] args) { Girl g1=new MMGirl(); //アップキャスト g1.smile(); MMGirl mmg=(MMGirl)g1; //ダウンキャスト、コンパイルも実行もエラーになりません mmg.smile(); mmg.c(); Girl g2=new Girl(); //MMGirl mmg1=(MMGirl)g2; //安全でないダウンキャスト、コンパイルは通りますが実行時にエラーになります // mmg1.smile(); // mmg1.c(); /*output: * CGirl smile sounds sweet... * CGirl smile sounds sweet... * CGirl c()... * Exception in thread "main" java.lang.ClassCastException: com.wensefu.other1.Girl * at com.wensefu.other1.Main.main(Girl.java:36) */ if(g2 instanceof MMGirl){ MMGirl mmg1=(MMGirl)g2; mmg1.smile(); mmg1.c(); } } }
Girl g1=new MMGirl(); //向上转型
g1.smile();
MMGirl mmg=(MMGirl)g1; //向下转型,编译和运行皆不会出错

ここでのダウンキャストは安全です。g1 が子クラスのオブジェクトを指しているからです。

而

Girl g2=new Girl();
MMGirl mmg1=(MMGirl)g2; //不安全的向下转型,编译无错但会运行会出错

実行時エラー:

Exception in thread "main" java.lang.ClassCastException: com.wensefu.other1.Girl
    at com.wensefu.other1.Main.main(Girl.java:36)

コードに示されているように、instanceof を使用して例外の発生を防ぐことができます。