2

試験の練習をしていて、完全に道に迷ってしまうサンプル問題を見つけました。次のコードについて、出力が何であるかを見つけます。

class Moe {
    public void print(Moe p) {
        System.out.println("Moe 1\n");
    }
}
class Larry extends Moe {
    public void print(Moe p) {
        System.out.println("Larry 1\n");
    }
    public void print(Larry l) {
        System.out.println("Larry 2\n");
    }
}
class Curly extends Larry {
    public void print(Moe p) {
        System.out.println("Curly 1\n");
    }
    public void print(Larry l) {
        System.out.println("Curly 2\n");
    }
    public void print(Curly b) {
        System.out.println("Curly 3\n");
    }
}
public class Overloading_Final_Exam {
    public static void main (String [] args) {
        Larry stooge1 = new Curly();
        Moe stooge2 = new Larry();
        Moe stooge3 = new Curly();
        Curly stooge4 = new Curly();
        Larry stooge5 = new Larry();
        stooge1.print(new Moe()); 
        ((Curly)stooge1).print(new Larry()); 
        ((Larry)stooge2).print(new Moe()); 
        stooge2.print(new Curly()); 
        stooge3.print(new Curly()); 
        stooge3.print(new Moe()); 
        stooge3.print(new Larry()); 
        ((Curly)stooge3).print(new Larry()); 
        ((Curly)stooge3).print(new Curly()); 
        stooge4.print(new Curly()); 
        stooge4.print(new Moe()); 
        stooge4.print(new Larry()); 
        stooge5.print(new Curly()); 
        stooge5.print(new Larry()); 
        stooge5.print(new Moe()); 
    }
}

私は自分のアイデアを念頭に置いていましたが、Java を実行すると、まったく異なる結果が得られました。

カーリー 1
カーリー 2
ラリー 1
ラリー 1
カーリー 1
カーリー 1
カーリー 1
カーリー 2
カーリー 3
カーリー 3
カーリー 1
カーリー 2
ラリー2
ラリー2
ラリー 1

最初のいくつかはOKですが、その後は本当に理解できません。誰でもこの問題について良い説明がありますか?

ありがとう

4

3 に答える 3

5

絵を描くことから始めようと...

Moe - print(Moe)
 |
Larry - print(Moe), print(Larry)
 |
Curly - print(Moe), print(Larry), print(Curly)

次に、変数を追跡します。

  • ラリー - stooge1 -> カーリー
  • 萌え - stooge2 -> ラリー
  • 萌え - stooge3 -> カーリー
  • カーリー - stooge4 -> カーリー
  • ラリー - stooge5 -> ラリー

  • stooge1.print(new Moe())

    • stooge1 -> Curly なので Curly.print(Moe) を呼び出します
  • ((Curly)stooge1).print(new Larry());

    • stooge1 -> Curly は Curly.print(new Larry()) を呼び出します
  • ((Larry)stooge2).print(new Moe());

    • stooge2 -> Larry は Larry.print(new Moe()); を呼び出します。
  • stooge2.print(new Curly());
    わかりました、ここで少しトリッキーになります (申し訳ありませんが、ここで 1 つ停止しました)。

    • stooge2 は Moe であると宣言されています。そのため、コンパイラは何を呼び出すかを調べているときに、print(Moe) メソッドを呼び出します。次に、実行時に stooge2 が Larry であることを認識し、Larry.print(Moe) メソッドを呼び出します。

等...

それを最後まで実行してもうまくいかない場合はお知らせください。

(次のものを明確にするために更新)

したがって、一般的なルールは次のとおりです。

  • コンパイラは変数の型を見て、呼び出すメソッドを決定します。
  • ランタイムは、変数が指している実際のクラスを調べて、メソッドをどこから取得するかを決定します。

したがって、次の場合:

Moe stooge2 = new Larry();
stooge2.print(new Moe());

コンパイラは次のように述べています。

  • Larry を stooge2 に割り当てることはできますか? (ラリーは萌えのサブクラスなので)
  • Moe には print(Moe) メソッドがありますか? (はい)

ランタイムは言う:

  • この here オブジェクトで print(Moe) メソッドを呼び出すことになっています... stooge2
  • stooge2 はラリーを指しています。
  • Larry クラスの print(Moe) メソッドを呼び出します。

すべての作業が完了したら、いくつかのメソッドを削除して、それがどのように変化するかを確認してください。

于 2009-03-16T01:06:17.840 に答える
2

実際、Java は静的で動的にバインドされているため、この問題は見かけほど単純ではありません。この演習から得られるすべての結果を理解する前に、それぞれがどこに適用されるかを理解する必要があります。

TofuBeer が言及した一般的な規則は、動的バインディングの場合にのみ正しいものです。静的バインディングでは、決定はコンパイル時にのみ行われます。

あなたの例では、動的バインディング(メソッドがオーバーライドされる場合)と静的バインディング(メソッドがオーバーロードされる場合)が混在しています。

詳細については、この質問をご覧ください。

于 2009-03-16T13:36:14.307 に答える
0

オブジェクトを見るときのヒントは、左の値を無視することです。代わりに、宣言中に右の値を見てください。これはオブジェクトの実際の値です。

于 2009-03-16T01:06:11.927 に答える