- ベストアンサー
継承とオーバーライド
サブクラスのインスタンスを、スーパークラスの変数に代入するときの考え方が分かりません。下記のプログラムを実行すると x: 10 Sub という結果にります。 spで、message()を呼び出してるのだから、Superクラスのmessage()が処理されるのではないでしょうか? また、コメントアウトすると、コンパイルエラーになる理由もわかりません。 上記の答えのように、Subクラスのmessage()を参照できるのでしたら、printY()も参照できるんじゃないのかって思います。 基本的な質問かもしれませんが、よろしくお願いします。 class Super{ int x = 10; void printX(){ System.out.println("x:" + x); } void message(){ System.out.println("Super"); } } class Sub extends Super{ int y; void printY(){ System.out.println("Y:" +y ); } void message(){ System.out.println("Sub"); } } class ExtensSample01{ public static void main(String[] args){ Super sp = new Sub(); sp.printX(); // sp.printY(); sp. message(); } }
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
>オーバーライドの時は、サブクラスのメソッドを参照すると覚えておくことにします。 そうですね。それで問題ないと思います。 せっかくなので、「Subクラスのmessage()を参照できるのでしたら、printY()も参照できるんじゃないのかって思います。」のポイントも書いておきます。 動物が道具を使えない説明はNo.4でしましたが、実際は人間である道具を使わせるためには(これまたNo.2で説明しましたが) ((人間)A).道具を使う(); と書きます。つまり、((人間)A)の部分で「Aは人間だぞ」と解釈させたので、人間に道具を使わせることが可能になります。 ここで、道具は使えないけど、動くことは進歩した class サル extends 動物{ void 動く(){ System.out.println("すたすた"); } } というクラスを作りたいと思います。 すると、 動物 B=new サル(); B.呼吸する(); B.動く(); というコードは書くことができますが、 ((サル)B).道具を使う(); というコードは書くことができません。サルは道具が使えないので、当たり前ですね。 では、 ((人間)B).道具を使う(); というコードは書くことができるのでしょうか?Bはサルを入れているのだから書くことができないと思われるかもしれません。しかし、実はこのコードは書くことができます。 コンパイラはBは動物であることはわかっても、中身が実際はサルであるか、人間であるかまでは認識していないのです。 で、実行してみるとわかると思いますが、(人間)Bという部分でClassCastExceptionが発生します。つまり、実行段階で動物Bは人間ではなくサルだったということを認識するわけです。 ポイントは 4)人間に道具を使わせたいときは一旦キャストして"人間だ"と思い込ませてから使う 5)コンパイラは動物であることまでは認識してても、中身がサルか人間かまでは把握していないため、その動物が実際にはサルであっても"人間だ"と思い込ませることができる 6)サルを"人間だ"と思い込ませたとき、コンパイラは「人間と思い込んでいるサル」に対して、「人間よ、道具を使え!」という命令ができる 7)ただし、実行段階で、実は人間ではなくサルであったということが"キャストの段階で"わかるため、ClassCastExceptionが発生する です。 じゃ、最初から 人間 A=new 人間(); や サル B=new サル(); って書けばいいだけの話でしょ?とか思われるかもしれませんが、この辺りはデザインパターンの「TempleteMethodパターン」を勉強するとより理解が深まると思います。
その他の回答 (4)
- freedom560
- ベストアンサー率46% (80/173)
>>System.out.println(sp.getClass().getName()); >ここの意味がよく分かりませんでした。 sp.getClass()でspのClassクラスを取ってきます。つまり、spの実体は何クラスですか?ということを取得するメソッドです。で、getName()はその名前を取ってくるメソッドです。つまり、sp.getClass().getName()でspの実体のクラス名を取ってきます。実際にこの1行を加えて実行してもらえましたか?"Sub"という文字が出力されたはずです。 他の部分はわかってもらえたのでしょうか? class 動物{ void 呼吸する(){ System.out.println("酸素~"); } void 動く(){ System.out.println("もぞもぞ"); } } class 人間 extends 動物{ void 道具を使う(){ System.out.println("ちょきちょき"); } void 動く(){ System.out.println("すたすた"); } } class AnimalTest{ public static void main(String[] args){ 動物 A = new 人間(); A.呼吸する(); // A.道具を使う(); A.動く(); } } と書き換えます。 ここで、「動物 A = new 人間();」という部分に入るAは「あくまで動物である」ということに注意してください。つまり、動物には「呼吸する」「動く」ということができるので、 A.呼吸する(); (動物よ、呼吸しなさい) A.動く(); (動物よ、動きなさい) という命令ができます。しかし、動物は「道具を使う」ことを知らないので、 A.道具を使う(); (動物よ、道具を使いなさい) という命令はできないのです。 で、オーバーロードの話ですが、 A.動く(); (動物よ、動きなさい) という命令を出したときに、Aは単なる動物ではなく、「人間」なので、"もぞもぞ"動こうとするのではなく、"すたすた"動きます。 つまり、この問題のポイントは 1)「動物 A = new 人間();」では、動物Aとして人間を代入しているが、Aはあくまで動物として解釈される 2)Aはあくまで動物であるので、動物が実行できるメソッドしか命令できない 3)動物に対して命令した後は、その動物が独自の方法で(オーバーライドされていたらその方法で)命令を実行する という点がポイントです。
お礼
丁寧に回答していただきまして有難うございました。 Super sp = new Sub()の時のオーバーライドの考え方でずっと悩んでたんですが、オーバーライドの時は、サブクラスのメソッドを参照すると覚えておくことにします。 特に教えていただいた3つのポイント忘れないようにしておきます。
- nicepeace
- ベストアンサー率58% (7/12)
こんにちわ。あまり自信ないんですが、流す程度に読んでみてください。 これは理解するのではなく、さきにルールを覚えたほうが楽になると思います。 問題はSuper sp = new Sub();の部分にあります。 ここを色々な見方から見てみると Super型でsuper型を作ると メソッドはsuper型が使われる。 super型でsub型を作ると メソッドはsub型が使われる。 sub型でsub型を作ると メソッドはsub型が使われる。 sub型でsuper型は作れない。 のだいたい4つです。 またエラーになる原因はサブクラス固有のメソッドはスーパークラス型では使えないのです。 つまりは、子供は親が誰だか分かるが、親は子供が誰だかわからないのです。 だから親のメソッドは使えるけど、親は子供のメソッドは知らないですから使えないわけなんです。 あんまり自信ないですが。 分かりづらかったらすみません。
お礼
有難うございました。教えていただいた法則覚えておきます。
- freedom560
- ベストアンサー率46% (80/173)
>spで、message()を呼び出してるのだから、Superクラスのmessage()が処理されるのではないでしょうか? Super sp = new Sub(); の直後に System.out.println(sp.getClass().getName()); という一行を入れてみてください。 spはSubクラスのインスタンスであることが明確になるはずです。つまり、sp. message();はSubクラスのメソッドが実行されます。 スーパークラス・・動物 メソッド<動く> サブクラス・・人間 メソッド<動く>は2本足で歩くにオーバーライド、メソッド<道具を使う> として、人間と言う動物に<動く>というメソッドを実行させると2本足で歩き出す というイメージです。 >コメントアウトすると、コンパイルエラーになる理由もわかりません。 Super spとしているので、spはあくまでSuperクラスのメソッドしか実行できません。 上記の例で言うと動物には<道具を使う>というメソッドはないので動物に道具を使え!という命令はできないと言うイメージです。 道具を使わせたいときには人間に命令します。つまり、一旦キャストして ((Sub)sp).printY(); とすればコンパイル+実行できます。
補足
>System.out.println(sp.getClass().getName()); ここの意味がよく分かりませんでした。
Super sp = new Sub(); spの変数型はSuperクラスであっても、実態はSubクラスです。 Superクラスの皮をかぶったSubクラスとでも言いましょうか。 なので、オーバーライドされたmessageメソッドを実行したときの処理は、Subクラスになります。 一方、sp.printY()がコンパイルエラーになるのは、spの変数型であるSuperクラスにprintY()メソッドがないからです。 SubはSuperの皮をかぶっているので、呼び出すほうからはSubは意識できないのです。 インタフェースと実態を別々に考えると分かりやすいのではないでしょうか。
補足
sp.printY()がコンパイルエラーになるのはなんとなく理解できます。 ただ、message()メソッドのほうは まだ理解できないです。実態はSub クラスにあるっていうとこがイメージできなくて、実態がSubクラスにあるんだったら、printY()も・・・って頭の中がループしちゃいます。 根本的な考え方が理解できてないのかもしれませんね。
お礼
全くの初心者がjavaを勉強し始めて基本的な質問だったのに、詳しく回答していただきまして、有難うございました。 とりあえず、今回はキャストを使えば動物も道具を使えるというとこまでは、理解できました。キャストって基本データ型の時だけだと思ってましたので、いい勉強になりました。それから先の説明はもうちょと勉強してからもう一度、考えてみたいと思います。 また、基本的な質問をしてしまうかもしれませんが、よろしくお願いします。