- 締切済み
Javaのインスタンス化の構文のイメージができない
こんばんは。 Javaプログラマを勉強しています。 サンプルプログラムで、オブジェクトの作成時、左辺と右辺でクラス名が異なる場合、 その一文ではどういうことが行われているのかイメージができません。 そこから原点に戻って、オブジェクトを作成する時の構文の一語一語の意味が分からなくなっています。 テキストによくある簡単な例だと、 ----------------- A a = new A(); ----------------- という構文があります。 この場合は左辺のAと右辺のAが同じであるため、このような構文が普通だと思い、ずっと理解した気になっていました。 ところが、 ----------------- class A { … } class B extends A{ … } public class Main(){ public static void main(String[] args){ A a = new B(); … } ----------------- という構文が出てきた時、 A a = new B(); で、左辺のAと右辺のBは違っても大丈夫なの?何故違うの?どういう場合にこのような構文を使うの? などの疑問が出てきて、 そもそもインスタンス化の構文 A a = new A(); のそれぞれは何を意味しているのか判らなくなり、 どこからどうやって理解していけば良いのか途方にくれています。 aはインスタンス化したオブジェクトの変数名であることは理解しています。 左辺のAと、右辺のAとnew演算子をどう理解すれば良いのか悩んでいます。 自分も何が分からないかを上手く説明できないのですが、 よろしくお願いします。
- みんなの回答 (7)
- 専門家の回答
みんなの回答
- 中村 拓男(@tknakamuri)
- ベストアンサー率35% (674/1896)
No.5 お礼に対してです。 >「クラスを継承してクラスを作るのは良くない、 >インターフェイスできちんとメソッドを定義してあげて、 >インターフェイスを実装したクラスを作るだけにするのが良い」 これは言いたいことは理解できますが、かなり極端な意見です。 継承万能論の時代から、継承は正しく使わないと弊害があることが 知られるようになったとき、クリーンで多重継承が可能な インターフェースがもてはやされた時期もありました。 今は使い方が熟してきていて、継承もインターフェースも 適材適所の時代です。 なにがなんでもインターフェースを実装するだけでは 共通の実装を利用できないのであまり現実的ではないですし、 それでは実装できない設計も数多くありますので 広く学習されることをお薦めします。 最後に、おすすめ書籍を3冊 1) バートランド・メイヤーの「Object Oriented Software Construction(邦訳は オブジェクト指向入門)」 オブジェクト志向全般の解説書。オブジェクト志向という考え方に大きな影響を与えた名著です。 但しはっきりいって入門書ではありません。ちょっと敷居が高いです。 2) Design Patterns Explained(邦訳 オブジェクト志向のこころ) 今回の質問のレベルの方の入門によいかも。 3) Effective Java Java に特化した部分はこいつかな。 それでは頑張ってください。
- jjon-com
- ベストアンサー率61% (1599/2592)
>No.1のお礼でも書きましたが、 >「スーパークラス型の変数に、 >サブクラス型のオブジェクトを代入できる」 >という点が理解しきれていません。(ANo.3のお礼) 念のために,Animal obj1 も Cow obj2 も参照型の変数だ,というJavaの基本の理解は大丈夫でしたっけ。 int i = 2011; double d = (double)i; のような基本型の変数における型変換は,int型とdouble型それぞれバイト数も内部形式も異なる変数における型変換ですが, 上記の obj1 と obj2 はともにバイト数も内部形式も同じ,参照型の変数(ポインタ)です。 new Animal() や new Cow() で生成されるオブジェクトはそれぞれ,内部に有するフィールドもメソッドも異なるでしょうし,総バイト数も異なるでしょう。でも,オブジェクトを指し示すJavaのポインタ変数はすべて同バイト数・同内部形式です。 ですから, -------- >サブクラス型の変数をスーパークラス型の変数に >キャストできるのは、 >「そういうルールだからだ」という理解レベルです。 >この点についても理解しきれていません。(ANo.1へのお礼) その理解で別に間違っていないと思います。 オブジェクトを指すポインタ変数はすべて同バイト数・同内部形式ですから,原理的には「あらゆるクラス型の変数は,自分を含み他のどんなクラス型の変数にも自由に代入できる」はずです。 ただ,そのようなスパゲティプログラムを生み出すような混乱を招きたくないから,矛盾が起きない限りにおいて代入を認めるという「そういうルール」をJavaは科すことにしたのでしょう。 スーパークラス/サブクラスの関係があるなら,サブクラスはスーパークラスの性質を継承しています。であるならサブクラスの変数をスーパークラスの変数として扱うことに矛盾はありません。 -------- >・Animal obj = new Cow(); >・Animal obj = new Animal(); >・Cow obj = new Cow(); >の場合、全て異なる出力になるので、湧いた疑問ですが…。 >等価な構文ではないので、 >厳密には同じ動きはしないと思います。 >意識して使い分ける場面はあるのでしょうか? Animalのインスタンスメソッドで定義してCowにオーバライドを許したいのか, Animalのクラスメソッドで定義してCowにオーバライドさせたくないのか, Animalの抽象メソッド(abstract)で定義してCowでの実装を指示したいだけなのか, プログラマがそれを意識しているなら当然,使い分ける場面が存在します。 -------- >配列の話については、ピンときたものがありました。 ANo.3の回答と同じく私も,下位クラスのさまざまなオブジェクト群を,上位クラスの配列でまとめて扱い,同一のメッセージに対して異なるオブジェクトからそれぞれ異なる実行結果を得るポリモーフィズムの例が分かりやすいと思います。 図形クラスの場合でしたら,円・三角形・長方形に対して getArea()「面積を求めよ」メソッドを呼び出してみてはいかがですか。 class Q7175470 { public static void main(String[] args) { Cat neko = new Cat(); Pig buta = new Pig(); Cow ushi = new Cow(); Animal[] animals = new Animal[3]; animals[0] = neko; animals[1] = buta; animals[2] = ushi; for (int i = 0; i < animals.length; i++) { animals[i].cry(); } } } abstract class Animal { abstract void cry(); } class Cat extends Animal { void cry() { System.out.println("meow"); } } class Cow extends Animal { void cry() { System.out.println("moo"); } } class Pig extends Animal { void cry() { System.out.println("oink"); } }
お礼
追加の回答ありがとうございます。 お礼遅くなりまして、すみません。 > Animal obj1 も Cow obj2 も参照型の変数だ > obj1 と obj2 はともにバイト数も内部形式も同じ,参照型の変数(ポインタ) > オブジェクトを指し示すJavaのポインタ変数はすべて同バイト数・同内部形式です。 なるほど、この辺りの認識が曖昧でした。 現在、初めてC言語を教えてもらい、ポインタの知識も加わり、仰っている意味がよく分かるようになりました。 > オブジェクトを指すポインタ変数はすべて同バイト数・同内部形式ですから,原理的には「あらゆるクラス型の変数は,自分を含み他のどんなクラス型の変数にも自由に代入できる」はずです。 > 矛盾が起きない限りにおいて代入を認めるという「そういうルール」をJavaは科すことにしたのでしょう。 こちらの文章にも、目から鱗でした。 すとん、と腑に落ちました。
- 中村 拓男(@tknakamuri)
- ベストアンサー率35% (674/1896)
NO.3 への補足です。ではもう少し難しい 話をしてみます。 図形 図形1 = new 三角形(); という書き方だと 図形 のメソッドは呼び出せますが 三角形で追加したメソッドは呼び出せません。 図形で定義するメソッドをうまくえらんで、 例えば、SaveToStream() とか draw() とか move() とか、 全ての図形で共通のメソッドをうまくよういしてやれば、 大部分の処理は、図形型でかけるはずです。 ということは、図形の種類が増えても 基本的には処理に影響が及びません。 このような書き方をすることを OCP(Open Close Principle) といいます。 この原則はオブジェクトの種類の追加に対して プログラムの修正をとても小さくする効果があります。 そして、OCPするために型をグループ化(抽象化)する作業を CVA(共通性-可変性 分析) などと言ったりします。 これらは各種書籍で詳説されていますの、調べてみてください。 Javaはこうした考え方に忠実に作られた言語ですが、 知らなければ宝の持ち腐れでしょう。 知らなくてもプログラムは作れますが、あとのメンテナンスで、 特に機能拡張の手間がまるで違ってきます。
お礼
追加の回答ありがとうございました。 同僚の人に、 「クラスを継承してクラスを作るのは良くない、 インターフェイスできちんとメソッドを定義してあげて、インターフェイスを実装したクラスを作るだけにするのが良い」 という話をしてもらいました。 「現実はそんな風に綺麗に出来上がらないけど」 とも言っておりました。 2000年に大学の授業で初めてJavaに触れましたが、当時はごまかされたような説明しか受けることができず、 11年経っても、まだこのようなレベルでいることに憤りを感じています。 書籍を読んでも簡単なものはきちんと説明されておらず、 難しいものは読んでも理解がついていかず、 その中間の書籍に出会うことが中々できません。 その中で自分は、Javaアソシエイツ → Javaプログラマと資格試験の勉強をすることで、 自分が判らずにいる所を判るようになろう、としているのですが、 中々スムーズに階段を登ることができずにいます。 Javaやオブジェクト指向を理解するには、 どう順序立てて学んでいけば良いのか、シラバスができれば良いなと思います。
- jjon-com
- ベストアンサー率61% (1599/2592)
A a = new A(); というのは, A a; a = new A(); という2行を1行にまとめて書いたもの。 多くの入門書に倣って,クラスA を「動物」クラスとして説明するなら,次のようになります。 A a; 動物オブジェクトを参照するための変数 a を作った。 でもまだ動物オブジェクトの実体は何も指し示していない。 a = new A(); 動物クラスを実体化して,aがそれを指し示すようにした。 ---------------- class B extends A {....} A a = new B(); 多くの入門書に倣って,クラスA を「動物」クラス,クラスB を「イヌ」クラスとして説明するなら,次のようになります。 class イヌ extends 動物 {....} イヌは動物の一種である,イヌは動物の性質を継承している,したがって, 動物 a = new イヌ(); 動物オブジェクトを参照するための変数 a が, 動物の一種である「イヌ」オブジェクトも指し示すことができるわけです。
お礼
回答ありがとうございました。 > 動物 a = new イヌ(); > 動物オブジェクトを参照するための変数 a が, > 動物の一種である「イヌ」オブジェクトも指し示すことができるわけです。 やはり、この部分がどうしても腑に落ちません。 下記のサイトを見つけたのですが、こちらを読めば何となく分かりそうな気がしたので、一度このサイトをきちんと読んでみたいと思います。 http://www.kab-studio.biz/Programing/OOPinJava/07/02.html
- 中村 拓男(@tknakamuri)
- ベストアンサー率35% (674/1896)
これは継承という機能です。 ちょっと初等的な説明をしてみます。 例えば class 図形 があって class 三角形 extends 図形 class 円 extends 図形 class 長方形 extends 図形 としておけば 図形 図形1 = new 三角形(); 図形 図形2 = 図形1; とできます。つまり様々な種類の図形を 図形型の変数に代入できるのです。 これって 便利だと思いませんか? 例えばたくさんの図形を処理するプログラムで、図形を配列で 持ちたい時、図形の種類別でしか配列を作れなかったら 悲しいですよね。 このようにグループを代表するクラスを考え出すことを抽象化、 グループを代表するクラスから、個々のクラスを定義することを 継承といいます。 とっかかりととして、簡単に説明してみましたが、 いかがでしょうか? 詳しくは「継承」 で調べてみてください。
お礼
回答ありがとうございます。 配列の話については、ピンときたものがありました。 図形[ ] 配列図形 = new 図形[3]; 図形 図形1 = new 三角形(); 図形 図形2 = new 円(); 図形 図形3 = new 長方形(); 配列図形[0] = 図形1; 配列図形[1] = 図形2; 配列図形[2] = 図形3; という風にすれば、三角形・円・長方形クラスのオブジェクトを、 図形クラスの配列にまとめることができるということでしょうか。 確かに、 三角形 図形1 = new 三角形(); 円 図形2 = new 円(); 長方形 図形3 = new 長方形(); では、まとめにくそうなイメージです。 同僚の人に同様の質問を投げかけた時に、 「オブジェクト指向の考え方からすれば、 A a = new B(); と書く方が適切だ」 という言葉が何となく判った気がしました。 (同僚の人と話をしていた時は、"interface A"で話をしていましたが) No.1のお礼でも書きましたが、 「スーパークラス型の変数に、サブクラス型のオブジェクトを代入できる」 という点が理解しきれていません。
補足
追加で疑問がわきました。 図形 図形1 = new 三角形(); と 三角形 図形1 = new 三角形(); は似たような振る舞いをすると思いますが、 等価な構文ではないので、 厳密には同じ動きはしないと思います。 意識して使い分ける場面はあるのでしょうか? テキストで、 ----------- public class Sample5_6{ public static void main(String[] args){ Animal obj = new Cow(); System.out.println(obj.a); System.out.println(obj.b); obj.methodA(); obj.methodB(); } } class Animal{ int a = 10; static int b = 50; public void methodA(){ System.out.println("Animal : methodA()"); } public static void methodB(){ System.out.println("Animal : methodB()"); } } class Cow extends Animal{ int a = 100; static int b = 500; public void methodA(){ System.out.println("Cow : methodA()"); } public static void methodB(){ System.out.println("Cow : methodB()"); } } ----------- というサンプルがあり、 3行目が、 ・Animal obj = new Cow(); ・Animal obj = new Animal(); ・Cow obj = new Cow(); の場合、全て異なる出力になるので、湧いた疑問ですが…。 同僚の人に「業務じゃ、こんなの使わない。いじわる問題だ」と言われましたが…。
- Tacosan
- ベストアンサー率23% (3656/15482)
クラスB の宣言は 「class B extends A」 となっているんだけど, この意味は大丈夫?
お礼
回答ありがとうございます。 「BクラスはAクラスのサブクラスである」 「BクラスはAクラスを継承している」 という理解です。 「Javaプログラマ」は取得していませんが、 「Javaアソシエイツ」は取得しました。 その辺りのレベルです。
- 21 grams(@21gram)
- ベストアンサー率40% (2/5)
A a = new A(); このイコールは、等式のイコールでは無く、右辺の処理結果を左辺に代入するという、代入のイコールです。 加えて言えば、Aの形をした変数aに、処理結果(Bの形をしている)を代入する式になります。 A a = new B(); こちらは、基本的に使わない、と考えておいた方が良いと思います。 # どうしても必要な場合は、一旦Aの形にCASTしてから(変えてから)代入することになります。
お礼
早速の回答ありがとうございます。 > このイコールは、等式のイコールでは無く、右辺の処理結果を左辺に代入するという、代入のイコールです。 これについての認識は問題ありません。 > 加えて言えば、Aの形をした変数aに、処理結果(Bの形をしている)を代入する式になります。 これについては、 「Aクラス型で変数aのオブジェクトがある」という所までは、認識は合っていると思います。 「そのオブジェクトはAクラス型だが、中身はBクラスのオブジェクト」ということなのでしょうか? 表現が難しいですが…。 この辺りの理解が曖昧です。 > # どうしても必要な場合は、一旦Aの形にCASTしてから(変えてから)代入することになります。 B b = new b(); A a; a = (A)b; ということでしょうか? サブクラス型の変数をスーパークラス型の変数にキャストできるのは、 「そういうルールだからだ」という理解レベルです。 この点についても理解しきれていません。
補足
お礼の中の文章は B b = new B(); A a; a = (A)b; でした。 (1行目のnew b();は間違い)
お礼
追加の回答ありがとうございました。 お礼が遅くなり、すみません。 > 継承万能論の時代から、継承は正しく使わないと弊害があることが > 知られるようになったとき、クリーンで多重継承が可能な > インターフェースがもてはやされた時期もありました。 同僚の方にはJavaの世界で上の歴史があったことについて教えてもらいました。 > 今は使い方が熟してきていて、継承もインターフェースも > 適材適所の時代です。 なるほど、そうなんですね。 同僚の方から「オブジェクト指向を突き詰めると、case文もif文も要らなくなる」なんて、 先のレベルの話もされましたが、自分にはまだ理解が及ばないので、まずは現在の理解レベルの足場固めをしたいと思います。 > 最後に、おすすめ書籍を3冊 ありがとうございます。本の推薦はありがたいです。 「オブジェクト指向のこころ」を購入しました。 同僚の方が「重箱の隅を突くようなJavaプログラマの勉強より、デザインパターンを覚えた方が良いんじゃないかな」とつぶやいていたので、ちょうど良い本のような気がします。 「Effective Java」については↓のページを見て、気に留めていました。 http://d.hatena.ne.jp/shuji_w6e/20110306/1299427454 順に、読んで知識をつけていきたいと思います。