Java動的バインディング
-
19-08-2019 - |
質問
私は試験のために練習していますが、サンプルの問題が見つかりました。 次のコードでは、出力が何であるかを見つけます。
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を実行すると、まったく違うものが得られました:
Curly 1 Curly 2 Larry 1 Larry 1 Curly 1 Curly 1 Curly 1 Curly 2 Curly 3 Curly 3 Curly 1 Curly 2 Larry 2 Larry 2 Larry 1
最初のいくつかは問題ありませんが、実際には理解できません。 誰でもこの問題について良い説明がありますか?
ありがとう
解決
絵を描くことから始めます...
Moe - print(Moe)
|
Larry - print(Moe), print(Larry)
|
Curly - print(Moe), print(Larry), print(Curly)
その後、変数を追跡します:
- ラリー-stooge1-<!> gt;カーリー
- 萌え-stooge2-<!> gt;ラリー
- 萌え-stooge3-<!> gt;カーリー
- カーリー-stooge4-<!> gt;カーリー
-
Larry-stooge5-<!> gt;ラリー
-
stooge1.print(new Moe())
- stooge1-<!> gt; CurlyはCurly.print(Moe)を呼び出します
-
((Curly)stooge1).print(new Larry());
- stooge1-<!> gt; CurlyはそうCurly.print(new Larry())を呼び出します
-
((Larry)stooge2).print(new Moe());
- stooge2-<!> gt; LarryはLarry.print(new Moe());と呼びます;
-
stooge2.print(new Curly());
わかりました、これは少しトリッキーになります(申し訳ありませんが、私はここで前に停止しました)- stooge2はMoeであると宣言されています。そのため、コンパイラーが何を呼び出すかを見ているとき、print(Moe)メソッドを呼び出します。その後、実行時にstooge2がLarryであることが認識されるため、Larry.print(Moe)メソッドが呼び出されます。
etc ...
それを最後まで実行してもうまくいかない場合はお知らせください。
(次のものを明確にするために更新)
したがって、一般的なルールは次のとおりです。
- コンパイラは変数の型を見て、呼び出すメソッドを決定します。
- ランタイムは、変数が指す実際のクラスを見て、メソッドの取得元を決定します。
次の場合:
Moe stooge2 = new Larry();
stooge2.print(new Moe());
コンパイラによると:
- Larryをstooge2に割り当てることはできますか? (はい、ラリーは萌えのサブクラスです)
- Moeにはprint(Moe)メソッドがありますか? (はい)
ランタイムのメッセージ:
- このhereオブジェクトでprint(Moe)メソッドを呼び出すことになっています... stooge2
- stooge2はラリーを指します。
- Larryクラスのprint(Moe)メソッドを呼び出します。
すべての作業を終えたら、いくつかのメソッドを削除して、それがどのように変化するかを確認してください。
他のヒント
実際、この問題は見た目ほど単純ではありません。Javaは静的であり、動的にバインドされているからです。この演習から得られるすべての結果を理解する前に、それぞれが適用される場所を理解する必要があります。
TofuBeerが言及した一般規則は、動的バインディングの場合にのみ正しいです。静的バインディングでは、決定はコンパイル時にのみ行われます。
この例では、動的バインディング(メソッドがオーバーライドされる場合)と静的バインディング(メソッドがオーバーロードされる場合)を組み合わせています。
詳細については、この質問をご覧ください。
ヒントは、オブジェクトを見るときに左側の値を無視することです。代わりに、宣言中に右の値を見てください。これはオブジェクトの実際の値です。