せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Java】隠蔽とオーバーロードの違い、詳しく。 パーフェクトJava学習感想文 余談

 
隠蔽とオーバーロードの違いについて書いたら、師匠からメールがきたよ。

つっこみやなくて、質問なんですが

呼ばれるメソッドの実体は、
隠蔽時:変数の型で決まる
オーバーライド時:オブジェクトの型で決まる

この隠蔽時ってなんですか?
ちなみに、変数の型のように、コード上見えている型を
apparent type
っていって、実際の値の型を
actual type
っていいます。

で、apparent type で決まるメソッドボディて
なんやろうかと、疑問に思った次第っす

へー。
言われてみたら確かに、apparentタイプ(変数の型)で決まる挙動は珍しいよね。

珍しいと思ったからメモしてたんだけど、
他の人にも珍しいレベルであれば、ちょっと詳しく確認して見ましょう。

 
まずはJavaの仕様を確認

クラスがある特定の名前をもつフィールドを宣言した場合,そのクラスのスーパクラス及びスーパインタフェースの中の,それと同じ名前をもつ任意の及びすべてのアクセス可能なフィールドの宣言を隠ぺい(hide)(6.3.1)すると呼ぶ。

あるフィールド宣言が他のフィールド宣言を隠ぺいする場合,二つのフィールドが同じ型をもつ必要はない。

http://www.y-adagio.com/public/standards/tr_javalang/8.doc.htm#40898

これ以降も読んでみたけれども、隠蔽したフィールドを変数に代入するときは、
どの型と決まるのか、は記載がないように思える。

 
 

変数の隠蔽とは

継承したクラス(以下子クラス)で、継承元クラス(以下親クラス)と同名のフィールド変数を宣言する。

■変数を親クラスで宣言した場合
 ⇒親クラスのフィールド変数として認識される

■変数を子クラスで宣言した場合
 ⇒親クラスのフィールドが隠蔽される

例えば以下のコードはコンパイルエラーにならない。

public class Test {  
  class Base {
    int s = 100;
  }
  class Hoge extends Base {
    Boolean s = true;
  }
  public static void main(String[] args) {
    new Test().method1();
  }
  void method1() {
    Base obj = new Hoge();
    System.out.println(obj.s + 200);

    //これはコンパイルエラー。sはintと認識されている
    //System.out.println(obj.s.booleanValue());
  }
}

オーバーロードと違い、子クラスで宣言するフィールドの型は、親クラスでのフィールド変数の型に依存しない。よってobj.sの型の情報はapparentタイプからしか取得できない、というイメージ。
 
■実行結果

300

 
オーバーロード時は、
メソッド宣言時の返り値の型は親クラスで宣言したものと依存する。
だから以下はエラー。

public class Test {  
  class Base {
    String meth1(){};
  }
  class Hoge extends Base {
    void meth1(){};
  }
}

ふむふむ。