90

昨日、2時間の電話による技術面接を受けましたが(合格しました、woohoo!)、Javaでの動的バインディングに関する次の質問を完全に消し去りました。そして、私が数年前に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);
  }
}

出力は、オーバーライドされたメソッド内からの2つの別個のprintステートメントである必要があると断言しましたequals():att1.equals(t3)t3.equals(t3)。後者の場合は十分に明白であり、前者の場合、t1Object型の参照がありますが、Test型としてインスタンス化されるため、動的バインディングはオーバーライドされた形式のメソッドを呼び出す必要があります。

どうやらそうではありません。私のインタビュアーは私に自分でプログラムを実行するように勧めました、そして見よ、オーバーライドされたメソッドからの出力は1つだけでした:行でt3.equals(t3)

私の質問は、なぜですか?すでに述べたようt1に、タイプObjectの参照ですが(静的バインディングはObjectのequals()メソッドを呼び出すため)、動的バインディングは、インスタンス化されたタイプの参照に基づいて、メソッドの最も具体的なバージョンを呼び出すように処理する必要があります。私は何が欠けていますか?

4

12 に答える 12

82

Java は、オーバーロードされたメソッドに対して静的バインディングを使用し、オーバーライドされたメソッドに対して動的バインディングを使用します。あなたの例では、equals メソッドがオーバーロードされている (Object.equals() とは異なる param 型を持っている) ため、呼び出されたメソッドはコンパイル時に参照型にバインドされます。

ここでいくつかの議論

それが equals メソッドであるという事実は、それをオーバーライドする代わりにオーバーロードすることがよくある間違いであることを除けば、実際には関係ありません。これは、インタビューの問題に対する回答に基づいてすでに認識されています。

編集:ここでも良い説明です。この例は、代わりにパラメーターの型に関連する同様の問題を示していますが、同じ問題が原因です。

バインディングが実際に動的である場合、呼び出し元とパラメーターが Test のインスタンスである場合は、オーバーライドされたメソッドが呼び出されると思います。したがって、t3.equals(o1) は印刷されない唯一のケースです。

于 2008-11-26T21:33:10.880 に答える
26

equalsメソッドは、のメソッドをTestオーバーライドしません。パラメータタイプを見てください!クラスは、を受け入れるメソッドでオーバーロードしています。equalsjava.lang.ObjectTestequalsTest

メソッドがオーバーライドすることを意図している場合はequals、@Overrideアノテーションを使用する必要があります。これにより、コンパイルエラーが発生し、この一般的な間違いが指摘されます。

于 2008-11-26T19:35:06.110 に答える
7

興味深いことに、(クラス ファイルにコンパイルできる) Groovy コードでは、呼び出しの 1 つを除いてすべてが print ステートメントを実行します。(Test と Object を比較するものは、明らかに Test.equals(Test) 関数を呼び出しません。) これは、groovy DOES が完全に動的型付けを行うためです。これは、明示的に動的に型指定される変数がないため、特に興味深いものです。プログラマーは groovy が Java のことを行うことを期待しているため、これは有害であると見なされていることをいくつかの場所で読みました。

于 2008-11-26T19:47:18.597 に答える
5

Java はパラメーターの共分散をサポートせず、戻り値の型でのみサポートします。

つまり、オーバーライド メソッドの戻り値の型は、オーバーライドされたメソッドのサブタイプである可能性がありますが、パラメーターの場合はそうではありません。

Object の equals のパラメーターが Object の場合、equals をサブクラス内の他のものに配置すると、オーバーライドされたメソッドではなく、オーバーロードされます。したがって、そのメソッドが呼び出される唯一の状況は、T3 の場合のように、パラメーターの静的型が Test の場合です。

就職面接プロセスを頑張ってください!私が学生に教える通常のアルゴリズム/データ構造の質問ではなく、この種の質問をする会社で面接を受けたいです。

于 2008-11-26T19:39:00.360 に答える
4

重要なのは、equals()メソッドが標準に準拠していないという事実にあると思います。Objectオブジェクトではなく別のTestオブジェクトを取り込んでいるため、equals()メソッドをオーバーライドしていません。これは、実際には、オブジェクトオブジェクトがObject.equals(Object o)を呼び出しているときに、テストオブジェクトが与えられたときに、何か特別なことをするためにオーバーロードしただけであることを意味します。IDEを通してそのコードを見ると、テスト用の2つのequals()メソッドが表示されます。

于 2008-11-26T19:31:11.297 に答える
4

メソッドはオーバーライドされる代わりにオーバーロードされます。Equalsは、常にオブジェクトをパラメーターとして受け取ります。

ところで、Blochの効果的なjava(所有する必要がある)にこれに関する項目があります。

于 2008-11-26T19:34:47.123 に答える
4

しばらく検索した後、 Dynamic Binding (DD) とStatic Binding̣̣̣ (SB) に注意してください。

1.タイミング実行:(参考1)

  • DB: 実行時
  • SB: コンパイル時間

2. 用途:

  • DB: オーバーライド
  • SB: オーバーロード (静的、プライベート、最終) (Ref.2)

参照:

  1. どのメソッドが使用することを好む平均リゾルバーを実行する
  2. static、private、または final 修飾子でメソッドをオーバーライドできないため
  3. http://javarevisited.blogspot.com/2012/03/what-is-static-and-dynamic-binding-in.html
于 2012-03-16T09:19:41.630 に答える
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);
    }
}
于 2013-10-30T21:38:14.900 に答える
1

動的バインディングと静的バインディングに関する興味深い記事を見つけました。動的バインディングをシミュレートするためのコードが付属しています。それは私のコードをより読みやすくしました。

https://sites.google.com/site/jeffhartkopf/covariance

于 2012-07-12T20:04:17.190 に答える
0

ここでオーバーライドするという概念がないことは非常に明白です。メソッドのオーバーロードです。Object()Objectクラスのメソッドは、Object型の参照のequal()パラメーターを取り、このメソッドは、Test型の参照のパラメーターを取ります。

于 2010-11-16T09:46:51.990 に答える
0

「なんで?」の答え。それがJava言語の定義方法です。

ウィキペディアの共分散と反分散に関する記事を引用するには:

戻り型の共分散は、Java プログラミング言語バージョン J2SE 5.0 で実装されています。メソッドのオーバーライドでは、パラメーターの型がまったく同じ (不変) である必要があります。そうでない場合、メソッドは代わりに並列定義でオーバーロードされます。

他の言語は異なります。

于 2008-11-26T21:59:39.997 に答える