17

私のコードは次のとおりです。

class Foo {
  public int a=3;
  public void addFive() {
    a+=5;
    System.out.print("f ");
  }
}

class Bar extends Foo {
  public int a=8;
  public void addFive() {
    this.a += 5;
    System.out.print("b ");
  }
}

public class TestClass {
  public static void main(String[]args) {
    Foo f = new Bar();
    f.addFive();
    System.out.println(f.a);
  }
}

出力:

b 3

メソッドがオーバーライドされているため、この質問の出力が「b 13」ではなく「b 3」である理由を教えてください。

4

4 に答える 4

19

Fは type の参照でありFoo、変数は多態的ではないため、元f.aの変数を参照しFooます3

それを確認する方法は?

これをテストするには、aから変数を 削除できFooます。コンパイル時エラーが発生します

注:メンバー変数privateを作成し、アクセサーを使用してそれらにアクセスします


こちらもご覧ください

于 2012-08-15T17:54:09.907 に答える
16

Java では変数をオーバーライドできないため、実際には 2 つのa変数がFooありますBar。一方、addFive()メソッドはポリモーフィックであるため、変更しますBar.a(Bar.addFive()静的な型にもかかわらず呼び出さfれますFoo)。

しかし、最終的にアクセスするf.aと、この参照は既知のタイプの を使用してコンパイル中に解決されfますFoo。したがって、Foo.a決して触れられませんでした。

ところで、Java の非 final 変数は公開しないでください。

于 2012-08-15T17:56:33.757 に答える
12

このような質問で、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 のオブジェクトに割り当てられるmaintype の変数を宣言します。これは から継承されているため可能です。次に、プログラムは type の変数のpublic フィールドを参照します。FooBarBarFooaFoo

ここでの誤りは、オーバーライドと呼ばれる同じ種類の概念がクラス フィールドに適用されると信じることです。しかし、フィールドにはそのような概念はありません。クラスのパブリック フィールドは、aクラスのパブリック フィールドをオーバーライドBarするのではなく、非表示と呼ばれることを行います。名前が示すように、クラスのスコープ内で、がのフィールドとは関係のない 自身のフィールドを参照することを意味します。( JLS 8.4.8 - 継承、オーバーライド、非表示)aFooBaraBarFoo

それで、私たちが書いているとき、私たちはf.aどちらaを参照していますか? フィールドの解決は、オブジェクトの宣言型を使用してコンパイル時aに行われることを思い出してください。結果として、プログラムは「3」を出力します。fFoo

では、クラスにaddFive()メソッドを追加して、試験問題のようFooにクラスでオーバーライドしてみましょう。Barここではポリモーフィズムが適用されるため、呼び出しf.addFive()はコンパイル時ではなく object の実行時の型 (つまり ) を使用して解決されるfため、 Bar'b' と出力されます。

しかし、まだ理解しておかなければならないことがありaます。5 単位増加したフィールド が、値 '3' に固執するのはなぜでしょうか? ここで隠れて遊んでいます。Barこれは呼び出されるclass のメソッドであり、classBarでは、everyがの public フィールドaを参照するため、これは実際にインクリメントされるフィールドです。BaraBar

1) ここで、補助的な質問が 1 つあります。メソッドからBarのパブリック フィールドにアクセスするにはどうすればよいでしょうか。次のようなものでそれを行うことができます:amain

System.out.println( ((Bar)f).a );

これにより、コンパイラは as のフィールドのフィールド メンバーを強制的にa解決fBarますa

これにより、この例では「b 13」が出力されます

2) さらに別の質問:のフィールドではなく、そのスーパークラスの同名フィールドを参照するために、クラスのメソッドに隠れて回避するにはどうすればよいでしょうか? フィールド参照の前にキーワードを追加するだけでうまくいきます。addFive()BarBarasuper

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()を見つけて、明示的に使用する必要がなくなるためです。Barthis

でもまあ、this受験生がこの試験問題を解く手がかりだったのかもしれませんね!

于 2012-08-15T19:19:11.333 に答える
1

あなたがやっているので、クラスからのf.a値を取得します。メソッドを呼び出して値を取得した場合、たとえば、クラスから値を取得したことになります。aFoogetA()Bar

于 2012-08-15T18:37:40.413 に答える