昨日、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ステートメントである必要があると断言しました:t1.equals(t3)
およびt3.equals(t3)
。後者のケースは十分に明白であり、前者のケースでは、_t1
_がObject型の参照を持っている場合でも、Test型としてインスタンス化されるため、動的バインディングはメソッドのオーバーライドされた形式を呼び出す必要があります。
どうやら。私のインタビュアーは自分でプログラムを実行するように勧めましたが、見よ、オーバーライドされたメソッドからの出力はt3.equals(t3)
行に1つしかありませんでした。
私の質問は、なぜですか?既に述べたように、_t1
_はObject型の参照であるため(静的バインディングはObjectのequals()
メソッドを呼び出します)、動的バインディングshouldの呼び出しを処理します参照のインスタンス化されたタイプに基づくメソッドの最も具体的なバージョン。私は何が欠けていますか?
Javaは、オーバーロードされたメソッドに静的バインディングを使用し、オーバーライドされたメソッドに動的バインディングを使用します。あなたの例では、equalsメソッドはオーバーロードされています(Object.equals()とは異なるparam型を持っています)ので、呼び出されたメソッドはreferenceにバインドされますコンパイル時に入力します。
いくつかの議論 ここ
Equalsメソッドであるという事実は、それをオーバーライドする代わりにオーバーロードするという一般的な間違いを除いて、実際には関連性がありません。これは、インタビューの問題に対する回答に基づいて既に認識しています。
編集:良い説明 こちら も。この例は、代わりにパラメータータイプに関連する同様の問題を示していますが、同じ問題が原因です。
バインディングが実際に動的である場合、呼び出し元とパラメーターがTestのインスタンスである場合、オーバーライドされたメソッドが呼び出されると考えられます。したがって、t3.equals(o1)のみが印刷されません。
興味深いことに、Groovyコード(クラスファイルにコンパイル可能)では、呼び出しの1つを除くすべてがprintステートメントを実行します。 (テストとオブジェクトを比較するものは明らかにTest.equals(Test)関数を呼び出しません。)これは、groovy DOESが完全に動的な型付けを行うためです。これは、明示的に動的に型指定される変数を持たないため、特に興味深いものです。プログラマーはgroovyがJavaのことをすることを期待しているので、これは有害と考えられていることをいくつかの場所で読みました。
Javaは、戻り値の型でのみパラメーターの共分散をサポートしません。
つまり、オーバーライドするメソッドの戻り値の型は、オーバーライドされるメソッドのサブタイプである場合がありますが、それはパラメーターには当てはまりません。
ObjectのequalsのパラメーターがObjectの場合、サブクラスにequalsを他のものと一緒に配置すると、オーバーライドされたメソッドではなく、オーバーロードされます。したがって、そのメソッドが呼び出される唯一の状況は、T3の場合のように、パラメーターの静的タイプがTestの場合です。
就職面接プロセスで頑張ってください!私が学生に教える通常のアルゴリズム/データ構造の質問の代わりに、これらのタイプの質問をする会社で面接を受けたいです。
キーは、equals()メソッドが標準に準拠していないという事実にあると思います。Objectオブジェクトではなく、別のTestオブジェクトを取り込むため、equals()メソッドをオーバーライドしません。これは、実際には、オブジェクトにTestオブジェクトが与えられ、ObjectオブジェクトがObject.equals(Object o)を呼び出すときに、何か特別なことをするためにオーバーロードしただけであることを意味します。任意のIDEでコードを見ると、テスト用の2つのequals()メソッドが表示されます。
メソッドはオーバーライドされる代わりにオーバーロードされます。 Equalsは常にパラメーターとしてオブジェクトを取ります。
ちなみに、Blochの有効なJava(所有する必要があります))にこの項目があります。
Dynamic Binding(DD)およびStaticBinding̣̣̣(SB)の注意事項しばらく検索した後:
1。タイミング実行:(Ref.1)
2。に使用:
参照:
オーバーロードする代わりにオーバーライドする別のメソッドが追加された場合、実行時の動的バインディング呼び出しについて説明します。
/ *次のプログラムの出力は何ですか? * /
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);
}
}
動的バインディングと静的バインディングに関する興味深い記事を見つけました。動的バインディングをシミュレートするためのコードが付属しています。それは私のコードをより読みやすくしました。
「なぜ」という質問に対する答え。 Java言語の定義方法です。
共分散と反共分散に関するウィキペディアの記事 を引用するには:
戻り型の共分散は、Javaプログラミング言語バージョンJ2SE 5.0で実装されています。メソッドタイプをオーバーライドする場合、パラメータータイプはまったく同じ(不変)である必要があります。
他の言語は異なります。
ここでオーバーライドする概念がないことは非常に明確です。メソッドのオーバーロードです。 ObjectクラスのObject()
メソッドはObject型の参照のパラメーターを受け取り、このequal()
メソッドはTest型の参照のパラメーターを受け取ります。
こちらもご覧くださいSO質問、密接に関連しています: Overrideing the Java equals method quirk