• ベストアンサー

クラス変数/メソッドとインスタンス変数/メソッドの見え方について

Javaの言語仕様(?)についてお教え下さい。 参照変数の型がインスタンスのスーパークラスの型の時、クラス変数/メソッドとインスタンス変数/メソッドの見え方が納得いかずに困っています。 以下のような条件の時です。   ・クラスが2つあり、1つはもう1つのクラスを継承しています。     それぞれを「スーパー」「サブ」と以下で呼びます。   ・インスタンスは"サブ"の型です。   ・上記インスタンスへの参照変数は"スーパー"のクラスの型です。   ・"スーパー"、"サブ"ともに【同名】の「クラス」変数/メソッド、「インスタンス」変数/メソッドがあります。 この場合に、"サブ"のインスタンスを参照している"スーパー"の型の変数を介し、それらにアクセスしたらどれが見えるか?という疑問です。 実験結果では以下のようになりました。           [フィールド]  [メソッド]   [1.static ]  スーパーの   スーパーの   [2.非static]  スーパーの   サブの 納得いかないのは「2.非static」で「フィールド」が、「スーパーの」になるところです。 これも「サブの」になると思っていました。 なぜサブクラスのが見えないのでしょうか? 同名の変数なのでスーパークラスのはサブクラスので隠れされ、サブクラスのが見えると思っていたのですが。 参考書には以下のように書いてありました。   フィールドの場合、参照変数の型のものが見える。   メソッドの場合、インスタンスの型のものが見える。 私には不自然に感じられるのですが、「こういう仕様だから。」と納得するしか無いのでしょうか? 「なぜこうなるか」について説明がある文献、サイトなどありましたらお教えください。 参考までに以下が実験したサンプルコードと結果です。長くて申し訳ありません。 「サンプルコード」 public class Super { static int staticField = 10; int instanceField = 100; static void staticMethod() { System.out.println( "staticField = " + staticField ); } void instanceMethod() { System.out.println( "instanceField = " + instanceField ); } } public class Sub extends Super { static int staticField = 90; int instanceField = 900; static void staticMethod() { System.out.println( "staticField = " + staticField ); } void instanceMethod() { System.out.println( "instanceField = " + instanceField ); } } public class TestStatic { public static void main(String[] args) { // インスタンスはSub、参照変数もSub、という状態。 Sub sub = new Sub(); System.out.println( "実験1" ); System.out.println( "1.クラス変数      " + sub.staticField ); System.out.print( "2.クラスメソッド    " ); sub.staticMethod(); System.out.println( "3.インスタンス変数   " + sub.instanceField ); System.out.print( "4.インスタンスメソッド " ); sub.instanceMethod(); // インスタンスはSub、参照変数はSuper、という状態。 Super sup = new Sub(); System.out.println( "実験2" ); System.out.println( "5.クラス変数      " + sup.staticField ); System.out.print( "6.クラスメソッド    " ); sup.staticMethod(); System.out.println( "7.インスタンス変数   " + sup.instanceField ); System.out.print( "8.インスタンスメソッド " ); sup.instanceMethod(); } } 「結果」 実験1 1.クラス変数      90 2.クラスメソッド    staticField = 90 3.インスタンス変数   900 4.インスタンスメソッド instanceField = 900 実験2 5.クラス変数      10 6.クラスメソッド    staticField = 10 7.インスタンス変数   100 8.インスタンスメソッド instanceField = 900 納得が行かないのは7のところです。 以上よろしくお願いいたします。

  • Java
  • 回答数3
  • ありがとう数2

質問者が選んだベストアンサー

  • ベストアンサー
回答No.3

この辺りは「なんで?」、「どうして?」とかって考え出すと、かなり難解な所ですね。私自身、SJC-Pを受験する際も「『親 new 子』はOK。」みたいな感じで機械的に覚えていましたからね。(コバリアント[共変戻り値]とかも、何となしには分かるけど、未だに使いこなすレベルまでにはなってないなあ。) >「なぜこうなるか」について説明がある文献、サイトなどありましたらお教えください。 取り敢えずは、いつものようにお勧め過去ログ。 Javaのメモリの管理 http://oshiete1.goo.ne.jp/qa1797536.html staticのメリット、デメリット http://oshiete1.goo.ne.jp/qa1355755.html ただ、文章ばかりだと何がなんだかいまいち分からないと思うので、イラスト付きのはこっち。 HOME > Java開発Tips > Technology & Programming よくある誤解、あやふやな知識をQ&A形式で総整理! Javaの“常識”、“非常識” 第2回 言語仕様編 http://www.itarchitect.jp/technology_and_programming/-/27052-5.html JVM のメモリ構造 http://www.nextindex.net/java/perform/storage.html 用は、サブクラスによるオーバーライド(メソッドの再定義)っていうのは、具体的にはJVMスタックによって実現されているわけですね。 で、私の方はどうかというと、以下のサイトでも見ながら何か面白いものが作れるかなあと考えていたのでしたが、全然ダメでした。(まだまだ修行が足りないなあ・・。) com.sun.jdi インタフェース StackFrame http://java.sun.com/j2se/1.5.0/ja/docs/ja/guide/jpda/jdi/com/sun/jdi/StackFrame.html

参考URL:
http://www.gihyo.co.jp/books/syoseki-contents.php/4-7741-1361-1
xanadou
質問者

お礼

ご回答ありがとうございます。 おかげさまで   ・メソッドがオーバーライドされる   ・フィールドはオーバーライドされない というメカニズムは良く分かりました。 ただ、「フィールドはオーバーライドされない」の理由というか「思想」がやっぱりしっくりきません。 メソッドはポリモルフィズムの実現のためにオーバーライドされるのは自然だと思いますし、 ついでと言ってはなんですが、フィールドも同様にすればいいのにと感じてしまうので・・・ これはもうとりあえず「そういうもの」と受け入れることにします。 今までもオブジェクト指向で   「ルールは分かったが、なんでこんな?」 というのは色々ありました。 ですが「ある日突然腑に落ちた。納得できた。」という瞬間が来ました。 今回もそのうちそういう瞬間が来ることを信じてみます。 色々とご教示ありがとうございました。

その他の回答 (2)

  • isle
  • ベストアンサー率51% (77/150)
回答No.2

Java言語仕様によると、 ・同名のフィールド宣言によってスーパークラスのフィールド宣言は隠蔽される ・参照式の型によって示されるクラス内のフィールドにアクセスする とあります。 要するにフィールドはオーバーライドしないということです。 サブクラスのフィールドにアクセスするためには System.out.println( "7.インスタンス変数   " + ((Sub)sup).instanceField ); とする必要があります。 get/setメソッドを用意してフィールドに直接アクセスできないようにするのがよろしいわけです。

参考URL:
http://www.y-adagio.com/public/standards/tr_x_0005-1/classes.doc.html#40425
xanadou
質問者

お礼

ご回答ありがとうございます。 やっぱり結論としては「そういうもの」と受け入れるしか無いようですね。 言語仕様として明確に規定されているということで納得(諦め?)できました。 ありがとうございます。 それと私は「隠蔽される」と「オーバーライドされる」を混同し、同一視していたようです。 これも勉強になりました。 >get/setメソッドを用意してフィールドに直接アクセスできないようにするのがよろしいわけです。 アクセサという奴ですよね? 今回の件の仕様が正直納得いかない以上、罠にハマらないようにするためにこのアクセサを使うということを徹底したいと思います。 色々と情報・アドバイスをありがとうございました。

  • zionic
  • ベストアンサー率39% (31/79)
回答No.1

supにSubクラスのインスタンスを入れているようにみえるけど、 実際はSuperクラスのインスタンスになっているため。 「Super sup = new Sub()」という書き方はSubクラスからSuperクラスの定義だけを抜き出したインスタンスを作っている、というイメージを持つと不自然には見えないと思います。

xanadou
質問者

補足

ご回答ありがとうございます。 ですが、 >supにSubクラスのインスタンスを入れているようにみえるけど、 >実際はSuperクラスのインスタンスになっているため。 は違うのでは? そうだとすると以下の結果の説明がつかない気がします。   8.インスタンスメソッド instanceField = 900 これはSubクラスのインスタンス中のインスタンス変数を出力しているのだと思うのですが。

関連するQ&A

  • Java 静的メソッドとインスタンスメソッド

    静的メソッドとインスタンスメソッドの使い分けがよくわかりません。 私の認識は 静的メソッド:インスタンスメンバ変数を参照する必要がない処理 インスタンスメソッド:インスタンスメンバ変数を参照して行う処理 と思っています。 よって、例えばDAOを作成する場合、select、update、insertを実行するメソッドを作成しますが、これらはクラスのインスタンスメンバ変数を参照する必要がないので静的メソッドにするべきだと思っています。 しかし、本やネットのDAOのサンプルプログラムはインスタンスメソッドで作成されています。 これらは、private static String の変数(SQL文が記述されている)を参照していますが、インスタンスメンバ変数は参照していません。 なぜselect、update、insertのメソッドをインスタンスメソッドにする必要があるのかわかりません。 静的メソッドとインスタンスメソッドの使い分けの基準を教えていただけないでしょうか。 よろしくお願いします。

    • ベストアンサー
    • Java
  • javaの質問です。

    明日JavaのBronze試験を受けに行くのですが、全然わからずに困っています。 紫色のJavaプログラマBronzr[SE7]という本を使用しています。 本の模試の問題です。 37 Class Super{ static void method(){ System.out.println("Super"); } } Class Sub extends Super{ static void method(){ System.out.println("Sub"); } } Class Test { public static void main(String[] args){ Super obj = new Sub(); obj.method(); } } この問題はSuperが表示されるらしいのですが、理由がわかりません。 サブクラスをインスタンス化しているので、Subが表示されるというのなら理解できます。staticが関係あるのはなんとなくわかるのですが、、、、、、なぜSuperなのかがわかりません。 解答ではスーパークラスの型にサブクラスを入れているからと簡単に書いてあります。 詳しく説明していただけると助かります。

    • ベストアンサー
    • Java
  • 継承について

    以下のような問題(SJC-P試験)があり、 解説では考え方として ●メンバがフィールドなら「変数の型」 ●インスタンスメソッドなら「実際のオブジェクトの型」 ●クラスメソッドなら「変数の型」 とありました。 できればなぜこのような考え方(法則)になるか理解したいと思っています。 #当方、Javaプログラミング経験ゼロで、実際に下記のようなコーディングをするかどうかもわかりませんが、 #丸暗記だと実際のコーディングで使えそうにないので、できれば理解したいと思ってます。 下記問題は解説の考え方さえ丸暗記すれば解けるのかもしれませんが (なぜ解説のような考え方になるのか含め)教えていただけませんでしょうか。 ----- 【問題1】 class Super{  int d = 10;  void meth()System.out.println(d); } class Sub extends Super{  double d = 20.0;  void meth()System.out.println(d); } class Sample1{  public static void main(String[] args){  Super s = new Sub();  System.out.println(s.d);  s.meth(); } 【答え】 10 20.0 ----- 【問題2】 class Super{  static int d = 10;  static void meth()System.out.println(d); } class Sub extends Super{  static double d = 20.0;  static void meth()System.out.println(d); } class Sample2{  public static void main(String[] args){  Super s = new Sub();  System.out.println(s.d);  s.meth(); } 【答え】 10 10 ----- よろしくお願い致します。

    • ベストアンサー
    • Java
  • メインメソッドの記述について

    メインメソッドを記述する際に、   public static void main(String[] args){     ~~~~~~~~~~~~~   } と書くと思いますが、なぜpublicを付けなければならないのでしょうか? 簡単に考えるために、1つのクラスのみで実行してみたのですがやはりpublicをつけないとエラーになってしまします。 1つのクラスしかないので、どこからも参照されないからpublicでなくても良いと思ったのですが・・・ ちなみに以下の様なとても簡単なもので考えました。 class E_1{ static void main(String[] args){ System.out.println("実験です"); } }

  • ”メンバ指定”で表示させる順がわかりません

    一番したにかいたプログラムなんですが、 System.out.println("**********基本的なメンバ操作***********"); の次の表示させるコードは System.out.println("+++++++++++クラスメンバをインスタンス名で操作+++++++++++"); となっているのに、実行結果が **********基本的なメンバ操作*********** インスタンスメソッド: インスタンス変数=100クラス変数=300 インスタンスメソッド: インスタンス変数=200クラス変数=300 クラスメソッド;クラス変数=300 +++++++++++クラスメンバをインスタンス名で操作+++++++++++ d1のクラス変数=400 d2のクラス変数=400 MyClsのクラス変数=400 でした。 どういう順で処理されているのでしょうか。 超初心者です。変なことをたぶん聞いていると思いますが教えてください。 class MyCls { int ins_hensu = 0; static int cls_hensu = 0; public void ins_method() { System.out.println("インスタンスメソッド: インスタンス変数=" + ins_hensu + "クラス変数=" + cls_hensu); } public static void cls_method(){ System.out.println("クラスメソッド;クラス変数=" + cls_hensu); } } public class JaCls08{ public static void main(String args[]) { MyCls d1 = new MyCls(),d2 = new MyCls(); System.out.println("**********基本的なメンバ操作***********"); d1.ins_hensu = 100; d2.ins_hensu = 200; MyCls.cls_hensu = 300; d1.ins_method(); d2.ins_method(); MyCls.cls_method(); System.out.println("+++++++++++クラスメンバをインスタンス名で操作+++++++++++"); d1.cls_hensu =400; System.out.println("d1のクラス変数=" + d1.cls_hensu); System.out.println("d2のクラス変数=" + d2.cls_hensu); System.out.println("MyClsのクラス変数=" + MyCls.cls_hensu); } }

    • ベストアンサー
    • Java
  • 継承とオーバーライド

    サブクラスのインスタンスを、スーパークラスの変数に代入するときの考え方が分かりません。下記のプログラムを実行すると 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(); } }

    • ベストアンサー
    • Java
  • インスタンス変数とクラス変数の違い

    1つのクラスオブジェクト内のインスタンス全てで共有できる クラス変数 @@a と、 同じインスタンス内であればメソッドの定義を越えてその値を 参照したり、変更したりできるインスタンス変数 @a のスコープの違いをお手すきでしたらご指導願えませんか? あとクラス変数にはセッターやゲッター等のアクセスメソッドを 設定できるんですか? いろいろ知りたいです。

    • ベストアンサー
    • Ruby
  • 親クラスと子クラスのフィールドとメソッドについて

    親クラスと子クラスのフィールドとメソッドについて 以下のサンプルソースを実行した時の動作の原理について 教えてください。 oya型変数にkoクラスのインスタンスを作成した場合、 メソッドはkoクラスのものなのに、 フィールドはoyaクラスのものになるということが イマイチすっきり理解できません。 どういうことなんでしょうか。 ------------------------------------------------------- [ソース] public class exec { public static void main( String args[]){ oya obj = new ko(); System.out.println(obj.str_field); obj.disp_field(); } } public class oya{ String str_field="親実行"; public void disp_field(){ System.out.println(str_field); } } public class ko extends oya { String str_field = "子実行"; public void disp_field(){ System.out.println(str_field); } } ------------------------------------------------------- [実行結果] > 親実行 > 子実行 -------------------------------------------------------

    • ベストアンサー
    • Java
  • Runnableのインスタンス化について

    下記のコードについてです。 Runnableがインスタンス化されていますが、 どうしてそれが可能なのかが分かりません。 分からない点は 1.Runnableはインタフェースであるから、本来、直接インスタンス化は不可能であるはず。 2.しかし、Runnableはクラスライブラリjava.langパッケージに含まれている。 だからインスタンス化は可能なのかもしれない。 3.あるいは、下記のコードではメソッド内の無名クラスであるから、「new Runnable(){」の 部分でスーパークラスとしてのRunnableを継承したサブクラスを生成しているのかもしれない。 アドバイスをよろしくお願い致します。 public class Main{ public static void main(String[]args){ Runnable task = new Runnable(){ public void run(){ System.out.println("run"); } }; Thread thread = new Thread(task){ public synchronized void start(){ System.out.println("start"); } }; thread.start(); } }

    • ベストアンサー
    • Java
  • インスタンス変数について

    JAVA勉強中のものです。 インスタンス変数についてわからないのですが、 class Data{int d;} classSample{ public static void main(String[] args){ Data data1 =new Data(); Data data2 =new Data(); data1.d = 100; data2 = data1; data2.d = 200; System.out.println(data1.d); } } 実行すると200が表示されますが、new演算子によってできたオブジェクトの実体を参照する ID がdata1およびdata2に入るのは理解できるのですが、 インスタンス変数のdはメモリ上でどう設定されているかイメージがわきません。 わかる方どうぞ教えて下さい。

    • ベストアンサー
    • Java