質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

2回答

1535閲覧

【Java】一種の配列で複数のサブクラスのデータを扱う方法

etherwind

総合スコア28

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

1クリップ

投稿2022/11/12 09:19

編集2022/11/12 09:50

前提

学校の課題でJavaを学習中です。授業中に理解できなかった部分を休日に勉強したのですが、やり方があっているかどうかを知りたく、書き込みさせていただきます。

1つのスーパークラス(A)から、継承したサブクラスBとCがあります。BとCのサブクラスにセットしたNameデータをゲットして、printlnで表示する処理を配列をつかってやる、というお題です。

私はサブクラスBとサブクラスC、それぞれで使う配列を別々に宣言して用意したのですが、先生から1つの配列でやるように指示がありました。

しかし私の理解では、親クラスから子クラスへのアクセスはできないので、継承したサブクラスを使うなら、配列の宣言はどうしても2つになるのでは? と思いました。

### 最初につくったコード

class SuperA { public String name; public void setName(String nm) { this.name = nm; } public void getName() { System.out.println("SuperAの " + this.name + " です"); } } class SubB extends SuperA { public String name; public SubB() { this.name = "SubB this.name"; System.out.println(this.name + " を作成しました。"); } public void setName(String nm) { this.name = nm; } public void getName() { System.out.println("SubBの " + this.name + " です"); } } class SubC extends SuperA { public String name; public SubC() { this.name = "SubC this.name"; System.out.println(this.name + " を作成しました。"); } public void setName(String nm) { this.name = nm; } public void getName() { System.out.println("SubCの " + this.name + " です"); } } public class test1 { public static void main(String[] args) { SubB[] sb = new SubB[1]; SubC[] sc = new SubC[1]; String x = "太郎くん"; String y = "花子さん"; for (int i = 0; i < sb.length; i++) { sb[i] = new SubB(); sc[i] = new SubC(); } for (int i = 0; i < sb.length; i++) { sb[i].setName(x); sc[i].setName(y); } for (int i = 0; i < sb.length; i++) { sb[i].getName(); sc[i].getName(); } } }

実現したいこと

1つの配列でやるように書き換えてみました。
これであっているでしょうか?
※class SuperA、class SubB extends SuperA、class SubC extends SuperAの内容は同じで、mainの記述内容のみ違います。

class SuperA { public String name; public void setName(String nm) { this.name = nm; } public void getName() { System.out.println("SuperAの " + this.name + " です"); } } class SubB extends SuperA { public String name; public SubB() { this.name = "SubB this.name"; System.out.println(this.name + " を作成しました。"); } public void setName(String nm) { this.name = nm; } public void getName() { System.out.println("SubBの " + this.name + " です"); } } class SubC extends SuperA { public String name; public SubC() { this.name = "SubB this.name"; System.out.println(this.name + " を作成しました。"); } public void setName(String nm) { this.name = nm; } public void getName() { System.out.println("SubCの " + this.name + " です"); } } public class test04 { public static void main(String[] args) { SuperA[] sa = new SuperA[2]; sa[0] = new SubB(); sa[1] = new SubC(); String x = "太郎くん"; String y = "花子さん"; sa[0].setName(x); sa[1].setName(y); sa[0].getName(); sa[1].getName(); } }

知りたいこと(2つ)

1:コードが合っているかどうか

実行してみると「変数sa」の配列だけでクラスBとクラスC内の処理ができているので、処理的にはできていると考えていますが、いまいち理解が追いついていません。これであってるでしょうか?

2:宣言部分の考え方

SuperA[] sa = new SuperA[2];
sa[0] = new SubB();
sa[1] = new SubC();

この↑の部分の処理がよくわかりません。

・SuperA型の配列を作ります。名前は「sa」です(わかる)
・「= new SuperA[2]」の記述により要素数が2になるのはわかるんですが、また「SuperA」とわざわざ記述されていることの処理の意味がわかりません。
・「sa[0] = new SubB();」を見て私が思うのは、SuperA型の配列のsa[0]の中はSubB()で使うからSubB()で初期化する、ってことかな? です。

この宣言の部分はネットや教科書でも調べたのですが、記述の仕方が中心に解説されており、なんでそう書くのか、どんな処理をしているのか、という部分に関しては、私でもわかる記述に行き当たることができませんでした。どなたか噛み砕きまくった解説を所望します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

> 子クラスから親クラスへのアクセスはできない

出来ます。


まず、 SubB と SubC が全く SuperA の機能を使っていません。意味があるのは getName のオーバーライドだけです。
SuperA にフィールド name もセッター setName も public として存在しますので、それらを流用すれば SubB/C は以下のように簡単に出来ます。

java

1class SuperA { 2 public String name; 3 public void setName(String nm) { 4 this.name = nm; 5 } 6 public void getName() { 7 System.out.println("SuperAの " + this.name + " です"); 8 } 9} 10 11class SubB extends SuperA { 12 public SubB() { 13 System.out.println("SubB を作成しました。"); 14 } 15 @Override //オーバーライドしていることを明示 16 public void getName() { 17 System.out.println("SubBの " + name + " です"); 18 } 19} 20 21class SubC extends SuperA { 22 public SubC() { 23 System.out.println("SubC を作成しました。"); 24 } 25 @Override //オーバーライドしていることを明示 26 public void getName() { 27 System.out.println("SubCの " + name + " です"); 28 } 29} 30 31public class Main { 32 public static void main(String[] args) { 33 SuperA[] sa = new SuperA[2]; // = の左の SuperA は配列の型、右の SuperA は配列の実体の型. 普通は同じ. 34 sa[0] = new SubB(); 35 sa[1] = new SubC(); 36 37 String x = "太郎くん"; 38 String y = "花子さん"; 39 40 sa[0].setName(x); //SubB にある SuperA のメソッド 41 sa[1].setName(y); //SubC にある SuperA のメソッド 42 43 sa[0].getName(); //SubB にある、"superA のメソッドをオーバーライドした"メソッド 44 sa[1].getName(); //SubC にある、"superA のメソッドをオーバーライドした"メソッド 45 } 46}

実行結果

plain

1SubB を作成しました。 2SubC を作成しました。 3SubBの 太郎くん です 4SubCの 花子さん です

親クラスから子クラスへのアクセスはできないので、継承したサブクラスを使うなら、配列の宣言はどうしても2つになるのでは?

2つ必要では無いことは動作したコードが書けたことでお分かりになったと思いますが、クラスに親子関係があるのは、まさにこのように『親の代わりとして子を使える』為です。
車用の車庫には車なら何処のメーカーのでも入れる、みたいな感じです。(現実では大きさで入れなかったりしますがクラスはどんなに大きくても大丈夫です。逆に車が入るならバイクも入りそうですが、クラスでは入れられません。)

「sa[0] = new SubB();」を見て私が思うのは、SuperA型の配列のsa[0]の中はSubB()で使うからSubB()で初期化する、ってことかな?

私が見て思うのは、sa[0] が null から SubB に変わった、です。

投稿2022/11/12 09:41

編集2022/11/12 20:04
jimbe

総合スコア12632

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

etherwind

2022/11/12 09:51

ご指摘ありがとうございます。自分の疑問をうまく文章化できていないまま投稿しておりました。該当の部分は削除しました。
jimbe

2022/11/12 10:26

質問のコメントのほうで書けばよかったですね…質問を読み直して回答を修正します。
etherwind

2022/11/13 02:25

ご回答ありがとうございます。 >車用の車庫には車なら何処のメーカーのでも入れる、みたいな感じ >私が見て思うのは、sa[0] が null から SubB に変わった、です。 このイメージで、もやもやしていた部分がようやく繋がった感じになりました! あと、オーバーライドへのご指摘も、勉強中だったので助かりました!
guest

0

既に解決済みですが、理解の一助になればと思い回答させて頂きます。

1:コードが合っているかどうか

宿題、課題のように見受けられるので正誤については言及しませんが、要求は満たしていると思います。

SuperA型の配列を作ります。名前は「sa」です(わかる)

SuperA[] sa = new SuperA[2];

Java

1SuperA[] sa; 2sa= new SuperA[2];

とわけて記述できます。
では SuperA[] sa; は配列が作られているかというと配列自体は作らていません。
saという変数はSuperA型の配列を指し示すもの、だと宣言しているだけです。

・「= new SuperA[2]」の記述により要素数が2になるのはわかるんですが、また「SuperA」とわざわざ記述されていることの処理の意味がわかりません

SuperA型の配列インスタンスを作成したという意味です。
SuperA型オブジェクトを入れられる2つの入れ物をメモリ空間を確保しているイメージして差し支えないかと思います。
変数saはその配列インスタンスのメモリアドレスを参照しています。

SuperA[] sa_b = new SubB[2]とすることはできますが、この配列にはSubC型インスタンスは入れられません。
SuperAと複数のサブクラスのインスタンスを入れる配列を作りたいのであれば、SuperA型の配列インスタンス作成する必要があります。

・「sa[0] = new SubB();」を見て私が思うのは、SuperA型の配列のsa[0]の中はSubB()で使うからSubB()で初期化する、ってことかな? です。

sa[0]自体の定義は、”new SuperA[2]”で作成された状態から変わるわけではありません。
new SubB()はSubBインスタンスの作成と、(スーパークラスとSubBの)コンストラクタの実行だけです。
sa[0]というSuperA型インスタンスを入れられる配列の1番目に、空(null)だったとこに
new Sub()で作成されたSubBインスタンスを入れたというだけです。
(正確にはSubBインスタンスがメモリの別のとこに作られ、それを参照するアドレスが格納された)

Java

1SuperA[] sa = new SuperA[2]; 2sa[0] = new SubB(); 3sa[1] = new SubC();

の処理内容は(変数を介している以外は)下記とほぼ同義です。

Java

1SuperA[] sa = new SuperA[2]; 2// この時点でsa[0] ,sa[1]はnull 3SuperA sb = new SubB(); 4sa[0] = sb; 5SuperA sc = new SubC(); 6sa[1] = sc;

補足

スーパークラス型の配列にサブクラスのインスタンスを入れられることのメリットの1つとして、個別に配列を作成する必要がなく、新たなサブクラスSubD,SubE・・を作成したときも同様に同じ配列に入れられることで、最小限の変更に抑えられます。
サブクラス毎にしか配列が作ることができないとしたら、サブクラスを追加する度に配列と処理を追加しなければなりません。

例えば、配列中のインスタンスの名前を表示する処理は、getName()がスーパークラスに定義されていることで以下のようにまとめることができます。もし新たなサブクラスを配列に追加しても、この処理を変更する必要はありません。

Java

1for ( SuperA supera: sa ) { 2 supera.getName(); 3}

投稿2022/11/13 13:59

Crimson_Tide

総合スコア509

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問