4

この問題は、私と私の友人が試験のために勉強していたときに現れました。静的に割り当てられた変数のメソッドを呼び出すときの奇妙な動作に気付きました。

Code>Words では、次のようにします。

class C {

    public void a(double x) {}
}

class D extends C {
    void b() {} 
}

class F extends D {

    void a(int i) {} 
    void a(double d) {} 

今、やっている

D foo = new F();
foo.a(1);

これは何を与えるでしょうか?まあ.. Fでメソッドa(double)を実行します!!

これは私たちが考えたことです:

  1. プログラムは、静的型Dの aを探すことから始めます。そこには何もありません。
  2. そのスーパー クラスCに移動します。a(int)は検出されませんが、代わりにa(double)が検出されます。
  3. これが私が意図した署名 (つまり a(double)) であると判断しますが、最初に、このすべての検索の後、最初に動的型を見てみましょう!
  4. F でa(double)を実行します!

これは正しいです?これは、int から double への型変換が行われた場合に適合する可能性のあるメソッドを見つけるために階層を登ったことを意味します。この後、動的型にこの新しく解釈されたシグネチャがあるかどうかを確認します。

追加したら気づいた

void a(int) {}

**クラス Cでは、上記の呼び出しを実行するとFでa(int)が返されます!

誰かがこのプロセスに関与するメカニズムを説明できますか? コードがこのように実行/コンパイルされるのはなぜですか? この背後にある技術的な理由は何ですか? また、同様の状況に関して、他に注意すべき点はありますか ()

4

2 に答える 2

7

その理由は、Java が静的に型付けされているためです。引数型のディスパッチは、実行時ではなくコンパイル時に行われます。

aコードがコンパイルされると、コンパイラは、 static 型のオブジェクトで名前が付けられたメソッドを呼び出していることを認識しますD。で互換性のあるメソッドDを探し、単一のメソッド (から継承C) を見つけます。への仮想呼び出しを実行するコードを生成しますC.a(double)

実行時に、仮想呼び出しはオブジェクトの実際の型 (引数ではありません!) でディスパッチされるためF.a(double)、これは をオーバーライドするため、最終的に を呼び出しますC.a(double)

オブジェクトの実行時の型が、コンパイル時にわかっていれば有効だったはずの別のメソッドを持っているという事実FF関係ありません。この動作が必要な場合は、リフレクションが必要です。

を追加C.a(int)すると、コンパイラは で名前が付けられた 2 つの異なるメソッドaD認識し、オーバーロード規則に基づいて、int を取るメソッドを選択します。

于 2012-12-16T04:27:34.267 に答える
4

呼び出すメソッドは、実行時ではなくコンパイル時に解決されます。コンパイラはコンパイル時に foo が であることしか認識していないDため、 method しかないため、a(double)それが呼び出されるメソッドです。ただし、どのオブジェクトを正確に呼び出すa(double)かは動的に (実行時に) 行われます。 F.a(double)C.a(double)

これはシングル ディスパッチと呼ばれます。呼び出すメソッドは、呼び出されるオブジェクトでは動的ですが、引数の型では静的です。

そのメソッドをC完全に削除すると、 in が呼び出さa(int)Fず、そのメソッドが見つからないと言ってコンパイルに失敗するだけです。

于 2012-12-16T04:28:06.973 に答える