Javaの動的バインディングとメソッドのオーバーライド
-
11-07-2019 - |
質問
昨日、電話で2時間のテクニカルインタビューを行いました(うーん、合格しました!)数年前に私がTAだったときにこの概念を学部生に教えていたので、それは二重に不可解です。
これは私に与えられた問題です:
/* What is the output of the following program? */
public class Test {
public boolean equals( Test other ) {
System.out.println( "Inside of Test.equals" );
return false;
}
public static void main( String [] args ) {
Object t1 = new Test();
Object t2 = new Test();
Test t3 = new Test();
Object o1 = new Object();
int count = 0;
System.out.println( count++ );// prints 0
t1.equals( t2 ) ;
System.out.println( count++ );// prints 1
t1.equals( t3 );
System.out.println( count++ );// prints 2
t3.equals( o1 );
System.out.println( count++ );// prints 3
t3.equals(t3);
System.out.println( count++ );// prints 4
t3.equals(t2);
}
}
出力は、オーバーライドされた equals()
メソッド内からの2つの個別のprintステートメントである必要があると断言しました:at t1.equals(t3)
および t3 .equals(t3)
。後者のケースは十分明らかであり、前者のケースでは、 t1
がObject型の参照を持っている場合でも、Test型としてインスタンス化されるため、動的バインディングはメソッドのオーバーライドされた形式を呼び出す必要があります。
明らかにそうではありません。インタビュアーは自分でプログラムを実行するように勧めましたが、見よ、オーバーライドされたメソッドからの出力は t3.equals(t3)
の行に1つしかありませんでした。
では、なぜですか?既に述べたように、 t1
はObject型の参照ですが(したがって、静的バインディングはObjectの equals()
メソッドを呼び出します)、動的バインディングは >インスタンス化された型の参照に基づいて、メソッドの最も具体的なバージョンを呼び出すようにします。何が足りないのですか?
解決
Javaは、オーバーロードされたメソッドに静的バインディングを使用し、オーバーライドされたメソッドに動的バインディングを使用します。この例では、equalsメソッドはオーバーロードされています(Object.equals()とは異なるparam型を持っています)。呼び出されたメソッドは、コンパイル時に reference 型にバインドされます。
いくつかの議論こちら
equalsメソッドであるという事実は、それをオーバーライドするのではなくオーバーロードするという一般的な間違いである以外、実際には関係ありません。これは、インタビューの問題に対する回答に基づいて既に認識しています。
編集: こちらにも説明があります。この例では、代わりにパラメータタイプに関連する同様の問題を示していますが、同じ問題が原因です。
バインディングが実際に動的である場合、呼び出し元とパラメーターがTestのインスタンスである場合、オーバーライドされたメソッドが呼び出されると考えられます。したがって、t3.equals(o1)のみが印刷されません。
他のヒント
Test
の equals
メソッドは、 java.lang.Object
の equals
メソッドをオーバーライドしません。パラメータの型を見てください! Test
クラスは、 Test
を受け入れるメソッドで equals
をオーバーロードしています。
equals
メソッドをオーバーライドする場合、@ Overrideアノテーションを使用する必要があります。これにより、この一般的な間違いを指摘するコンパイルエラーが発生します。
興味深いことに、Groovyコード(クラスファイルにコンパイル可能)では、呼び出しの1つを除くすべてがprintステートメントを実行します。 (テストとオブジェクトを比較するものは明らかにTest.equals(Test)関数を呼び出しません。)これは、groovy DOESが完全に動的な型付けを行うためです。これは、明示的に動的に型指定される変数を持たないため、特に興味深いものです。プログラマーはgroovyがjavaのことをすることを期待しているため、これは有害であるといくつかの場所で読んでいます。
Javaはパラメーターの共分散をサポートせず、戻り値の型のみをサポートします。
つまり、オーバーライドするメソッドの戻り値の型は、オーバーライドされるメソッドのサブタイプである場合がありますが、パラメーターの場合はそうではありません。
ObjectのequalsのパラメーターがObjectである場合、サブクラスにequalsを他のものと一緒に配置すると、オーバーライドされたメソッドではなく、オーバーロードされます。したがって、そのメソッドが呼び出される唯一の状況は、T3の場合のように、パラメーターの静的タイプがTestの場合です。
就職の面接プロセスで頑張ってください!私が学生に教える通常のアルゴリズム/データ構造の質問の代わりに、これらのタイプの質問をする会社で面接を受けたいです。
キーは、equals()メソッドが標準に準拠していないという事実にあると思います。Objectオブジェクトではなく、別のTestオブジェクトを取り込むため、equals()メソッドをオーバーライドしていません。これは、実際には、オブジェクトを呼び出すときにObject.equals(Object o)を呼び出して、Testオブジェクトを与えたときに特別なことをするためにオーバーロードしただけであることを意味します。 IDEを介してそのコードを調べると、テスト用の2つのequals()メソッドが表示されるはずです。
メソッドはオーバーライドされる代わりにオーバーロードされます。 Equalsは常にオブジェクトをパラメーターとして受け取ります。
ところで、Blochの効果的なjavaにこのアイテムがあります(所有する必要があります)。
しばらくしてから動的バインド(DD)および静的バインド̣̣̣ (SB)のメモ:
1。タイミング実行:(Ref.1)
- DB:実行時
- SB:コンパイラー時間
2。使用対象:
- DB:オーバーライド
- SB:オーバーロード(静的、プライベート、最終)(参照2)
参照:
- どのメソッドを使用するかを選択する平均リゾルバを実行します
- modifier static、private、またはfinalでメソッドをオーバーライドできないため
- http:// javarevisited。 blogspot.com/2012/03/what-is-static-and-dynamic-binding-in.html
オーバーロードする代わりにオーバーライドする別のメソッドが追加された場合、実行時の動的バインディング呼び出しについて説明します。
/ *次のプログラムの出力は何ですか? * /
public class DynamicBinding {
public boolean equals(Test other) {
System.out.println("Inside of Test.equals");
return false;
}
@Override
public boolean equals(Object other) {
System.out.println("Inside @override: this is dynamic binding");
return false;
}
public static void main(String[] args) {
Object t1 = new Test();
Object t2 = new Test();
Test t3 = new Test();
Object o1 = new Object();
int count = 0;
System.out.println(count++);// prints 0
t1.equals(t2);
System.out.println(count++);// prints 1
t1.equals(t3);
System.out.println(count++);// prints 2
t3.equals(o1);
System.out.println(count++);// prints 3
t3.equals(t3);
System.out.println(count++);// prints 4
t3.equals(t2);
}
}
動的バインディングと静的バインディングに関する興味深い記事を見つけました。動的バインディングをシミュレートするためのコードが付属しています。コードが読みやすくなりました。
SO質問もご覧ください。密接に関連しています。 a>
質問への回答" why?"それがJava言語の定義方法です。
共分散と共分散に関するウィキペディアの記事を引用するには:
戻り型の共分散が実装されています Javaプログラミング言語で バージョンJ2SE 5.0。パラメータタイプには まったく同じ(不変) メソッドのオーバーライド、それ以外の場合 メソッドは並列でオーバーロードされています 代わりに定義します。
他の言語は異なります。
ここでオーバーライドする概念がないことは非常に明確です。メソッドのオーバーロードです。
Objectクラスの Object()
メソッドはObject型の参照のパラメーターを受け取り、この equal()
メソッドはTest型の参照のパラメーターを受け取ります。
オンラインで出会ったいくつかの例の拡張版である2つの例でこれを説明しようとします。
public class Test {
public boolean equals(Test other) {
System.out.println("Inside of Test.equals");
return false;
}
@Override
public boolean equals(Object other) {
System.out.println("Inside of Test.equals ot type Object");
return false;
}
public static void main(String[] args) {
Object t1 = new Test();
Object t2 = new Test();
Test t3 = new Test();
Object o1 = new Object();
int count = 0;
System.out.println(count++); // prints 0
o1.equals(t2);
System.out.println("\n" + count++); // prints 1
o1.equals(t3);
System.out.println("\n" + count++);// prints 2
t1.equals(t2);
System.out.println("\n" + count++);// prints 3
t1.equals(t3);
System.out.println("\n" + count++);// prints 4
t3.equals(o1);
System.out.println("\n" + count++);// prints 5
t3.equals(t3);
System.out.println("\n" + count++);// prints 6
t3.equals(t2);
}
}
ここで、カウント値が0、1、2、および3の行の場合。 equals()
メソッドに o1 および t1 の Object の reference があります。したがって、コンパイル時に、 Object.class ファイルの equals()
メソッドがバインドされます。
ただし、 t1 の reference は Object ですが、テストクラスの初期化があります。
Object t1 = new Test();
。
したがって、実行時に public boolean equals(Object other)
を呼び出します。これは
オーバーライドされたメソッド
今、カウント値が4および6の場合、Testの reference および initialization を持つ t3 が< code> equals()メソッドは、オブジェクト参照としてパラメーターを持ち、
オーバーロードされたメソッド
OK!
再び、コンパイラが呼び出すメソッドをよりよく理解するために、 メソッドをクリックすると、Eclipseは同様のメソッドを強調表示します コンパイル時に呼び出すと考えられる型。取得しない場合 コンパイル時に呼び出される場合、これらのメソッドはメソッドの例です オーバーライド。