こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

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

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のところです。

以上よろしくお願いいたします。

投稿日時 - 2007-10-30 15:42:59

QNo.3474622

困ってます

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

この辺りは「なんで?」、「どうして?」とかって考え出すと、かなり難解な所ですね。私自身、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

投稿日時 - 2007-11-01 00:46:03

お礼

ご回答ありがとうございます。

おかげさまで
  ・メソッドがオーバーライドされる
  ・フィールドはオーバーライドされない
というメカニズムは良く分かりました。

ただ、「フィールドはオーバーライドされない」の理由というか「思想」がやっぱりしっくりきません。
メソッドはポリモルフィズムの実現のためにオーバーライドされるのは自然だと思いますし、
ついでと言ってはなんですが、フィールドも同様にすればいいのにと感じてしまうので・・・
これはもうとりあえず「そういうもの」と受け入れることにします。

今までもオブジェクト指向で
  「ルールは分かったが、なんでこんな?」
というのは色々ありました。
ですが「ある日突然腑に落ちた。納得できた。」という瞬間が来ました。
今回もそのうちそういう瞬間が来ることを信じてみます。

色々とご教示ありがとうございました。

投稿日時 - 2007-11-01 15:09:52

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(3)

ANo.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

投稿日時 - 2007-10-31 07:11:22

お礼

ご回答ありがとうございます。

やっぱり結論としては「そういうもの」と受け入れるしか無いようですね。
言語仕様として明確に規定されているということで納得(諦め?)できました。
ありがとうございます。

それと私は「隠蔽される」と「オーバーライドされる」を混同し、同一視していたようです。
これも勉強になりました。

>get/setメソッドを用意してフィールドに直接アクセスできないようにするのがよろしいわけです。

アクセサという奴ですよね?
今回の件の仕様が正直納得いかない以上、罠にハマらないようにするためにこのアクセサを使うということを徹底したいと思います。

色々と情報・アドバイスをありがとうございました。

投稿日時 - 2007-10-31 10:50:15

ANo.1

supにSubクラスのインスタンスを入れているようにみえるけど、
実際はSuperクラスのインスタンスになっているため。

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

投稿日時 - 2007-10-30 18:09:11

補足

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

投稿日時 - 2007-10-30 22:01:08

あなたにオススメの質問