このような質問で、SCJP 試験は、隠蔽と呼ばれるものに関する知識を評価しています。審査官は、プログラムの動作がポリモーフィズムのみに依存していると信じ込ませるために、故意に物事を複雑にしました。
addFive()
メソッドを削除して、物事をもう少し明確にしてみましょう。
class Foo {
public int a = 3;
}
class Bar extends Foo {
public int a = 8;
}
public class TestClass {
public static void main(String[]args) {
Foo f = new Bar();
System.out.println(f.a);
}
}
これで、物事は少し混乱しなくなりました。このメソッドは、実行時に type のオブジェクトに割り当てられるmain
type の変数を宣言します。これは から継承されているため可能です。次に、プログラムは type の変数のpublic フィールドを参照します。Foo
Bar
Bar
Foo
a
Foo
ここでの誤りは、オーバーライドと呼ばれる同じ種類の概念がクラス フィールドに適用されると信じることです。しかし、フィールドにはそのような概念はありません。クラスのパブリック フィールドは、a
クラスのパブリック フィールドをオーバーライドBar
するのではなく、非表示と呼ばれることを行います。名前が示すように、クラスのスコープ内で、がのフィールドとは関係のない 自身のフィールドを参照することを意味します。( JLS 8.4.8 - 継承、オーバーライド、非表示)a
Foo
Bar
a
Bar
Foo
それで、私たちが書いているとき、私たちはf.a
どちらa
を参照していますか? フィールドの解決は、オブジェクトの宣言型を使用してコンパイル時a
に行われることを思い出してください。結果として、プログラムは「3」を出力します。f
Foo
では、クラスにaddFive()
メソッドを追加して、試験問題のようFoo
にクラスでオーバーライドしてみましょう。Bar
ここではポリモーフィズムが適用されるため、呼び出しf.addFive()
はコンパイル時ではなく object の実行時の型 (つまり ) を使用して解決されるf
ため、 Bar
'b' と出力されます。
しかし、まだ理解しておかなければならないことがありa
ます。5 単位増加したフィールド が、値 '3' に固執するのはなぜでしょうか? ここで隠れて遊んでいます。Bar
これは呼び出されるclass のメソッドであり、classBar
では、everyがの public フィールドa
を参照するため、これは実際にインクリメントされるフィールドです。Bar
a
Bar
1) ここで、補助的な質問が 1 つあります。メソッドからBar
のパブリック フィールドにアクセスするにはどうすればよいでしょうか。次のようなものでそれを行うことができます:a
main
System.out.println( ((Bar)f).a );
これにより、コンパイラは as のフィールドのフィールド メンバーを強制的にa
解決f
しBar
ますa
。
これにより、この例では「b 13」が出力されます。
2) さらに別の質問:のフィールドではなく、そのスーパークラスの同名フィールドを参照するために、クラスのメソッドに隠れて回避するにはどうすればよいでしょうか? フィールド参照の前にキーワードを追加するだけでうまくいきます。addFive()
Bar
Bar
a
super
public void addFive() {
super.a += 5;
System.out.print("b ");
}
これにより、この例では「b 8」が出力されます。
最初のステートメントに注意してください
public void addFive() {
this.a += 5;
System.out.print("b ");
}
に洗練することができます
public void addFive() {
a += 5;
System.out.print("b ");
}
コンパイラが field を解決するときa
、メソッド内から最も近い外側のスコープを調べ始め、クラス インスタンスaddFive()
を見つけて、明示的に使用する必要がなくなるためです。Bar
this
でもまあ、this
受験生がこの試験問題を解く手がかりだったのかもしれませんね!