- ベストアンサー
変数の継承について
初めまして。最近、独習Javaで勉強を始めた者です。ゆっくりながら継承の所まで進みました。 abstract class Monster{ String monster; } class Vampire extends Monster{ String monster = "Vampire"; } class Zombie extends Monster{ String monster = "Zombie"; } class NewSougou3{ public static void main(String args[]){ Vampire vam = new Vampire(); Zombie zom = new Zombie(); Monster array[] = new Monster[2]; array[0] = vam; array[1] = zom; for (int i = 0 ; i < 2 ; i++){ System.out.println(array[1].monster); } } } というプログラムを作ったのですが、実行すると"null"が二つ出てきただけで正しい"Vampire"と"Zombie"が表示されません。Monsterクラスで定義したmonsterの値(既定)が表示されていると思うのですが、Monster型で宣言した配列にVampire、Zombieクラスをインスタンス化して代入してもそれは反映されないのでしょうか?スーパークラスであるMonsterクラスで変数monsterを宣言し、二つのクラスをインスタンス化した時に変数monsterにそれぞれの値が代入されると思ったのですが… なにぶん始めたばかりなので見当違いのことを質問しているかもしれません。その辺はお許し下さい。よろしくお願いします。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
> しかし、.monsterの指しているモノがわかりにくいのです。 確かに混乱しますよね。でも、実は単純なことなんですよ。 .monster が何を指しているかは変数の型で決まります。 vam は Vampire 型の変数として宣言されているので、vam.monster は Vampire クラスの monster を指すことになります。 一方 array は Monster 型の配列として宣言されているので、array[0].monster や array[1].monster は Monster クラスの monster を表すことになります。 質問者さんが最初に作ったプログラムでは、Monster クラスと Vampire クラスとでそれぞれ monster フィールドが宣言されていました。そのため、vam.monster と array[0].monster もそれに応じて別なフィールドを指すことになったのです。 私が前の回答で書いたプログラムでは、monster フィールドは Monster クラスでだけ宣言されています。この場合、Vampire クラスそのものには monster フィールドは宣言されていないので、vam.monster は Vampire クラスが Monster クラスから継承した monster を指すことになります。つまり、vam.monster と array[0].monster は同じ物を指すということです。
その他の回答 (2)
- UKY
- ベストアンサー率50% (604/1207)
これでは、MonsterクラスとVampireクラスに《それぞれ》monsterというフィールドを与えたことになります。 つまり、親クラス(抽象クラス)と子クラス(具象クラス)の両方に同じ名前の別々なフィールドが宣言されていると言うことです。 このプログラムでは、子クラスのフィールドには文字列が代入されていますが、親クラスのフィールドはnullのままです。 今回の場合、子クラスでも別なフィールドを宣言したのが誤りです。子クラスでは新たなフィールドを宣言せずに、(親クラスの)フィールドに代入のみを行うのです。 abstract class Monster{ String monster; } class Vampire extends Monster{ Vampire() { monster = "Vampire"; } } class Zombie extends Monster{ Zombie() { monster = "Zombie"; } } class NewSougou3{ 《変更なし》 } あるいは、次のように書くこともできます。 抽象クラスに引数ありのコンストラクタを用意することで、サブクラスのコンストラクタは抽象クラスのコンストラクタを明示的に呼び出すことが必要となります。抽象クラスのコンストラクタで確実に代入が行われるので、サブクラスの書き間違いによる代入のし忘れミスを防げます。 abstract class Monster{ String monster; Monster(String name) { monster = name; } } class Vampire extends Monster{ Vampire() { super("Vampire"); } } class Zombie extends Monster{ Zombie() { super("Zombie"); } }
お礼
遅くなってすいません。回答ありがとうございます。サブクラスで実際に値を代入すればオーバーライドされるものと思っていました。 (1)Vampire vam = new Vampire(); (2)Zombie zom = new Zombie(); (3)Monster array[] = new Monster[2]; (4) array[0] = vam; (5) array[1] = zom; (1)のところではVampire型のmonsterを宣言していますよね?この時点ではvam.monsterは"Vampire"を指しているのですよね?そのvamをMonster型の配列に代入すると((4)のところ)array[0].monsterはMonster型のmonsterつまり"null"を指すようになるということでしょうか?わかりにくい質問で申し訳ありません。 示していただいた回答例の内容は理解する事ができます。次からはそのようにしていこうと思います。しかし、.monsterの指しているモノがわかりにくいのです。
- anmochi
- ベストアンサー率65% (1332/2045)
変数ってabstructにできないような気がする。 変数のオーバーライドって行われないのね。 簡単な原理を説明すると、Vampireクラスのインスタンスを作ると、その中には変数が2つあるって事だ。this.monsterとsuper.monsterだね。つまり、クラスのインスタンスって、java.lang.Objectからの全ての変数を中に持ってるってわけ。 で、mainメソッドの中では、array[1].monsterは「Monsterクラスのmonster」なので、nullが表示されるんだな。 ところで、Javaではメソッドを作ると基本が(C++で言うところの)virtualになるので、必ずしもabstructクラスやabstructメソッドにする必要はない(クラス設計による)。 結論としては、こういう使い方をしたいなら変数ではなくてメソッドにしなければならない。
お礼
遅くなってすいません。回答ありがとうございます。変数はabstructにはできないのですね?まだまだ勉強が足りないようです。ありがとうございました。
お礼
わかりやすい回答ありがとうございます。プログラムをかいている途中で自分の都合のいいように勝手に解釈して思いこみで進んでしまっている時があります。これからはUKYさんが示して頂いた方法でやっていこうと思います。ありがとうございました。