2

次のコードがあるとしましょう

class TestEqual{
    public boolean equals(TestEqual other ) {
    System.out.println( "In equals from TestEqual" ); return false;
}

    public static void main( String [] args ) {
        Object t1 = new TestEqual(), t2 = new TestEqual();
        TestEqual t3 = new TestEqual();
        Object o1 = new Object();

        int count = 0;
        System.out.println( count++ );// shows 0
        t1.equals( t2 ) ;
        System.out.println( count++ );// shows 1
        t1.equals( t3 );
        System.out.println( count++ );// shows 2
        t3.equals( o1 );
        System.out.println( count++ );// shows 3
        t3.equals(t3);
        System.out.println( count++ );// shows 4
        t3.equals(t2);
    }
}

基本的に、TestEqual クラス (もちろん Object を拡張します) には、Object から equals メソッドをオーバーロードするメソッドがあります。

また、いくつかの変数があります。TestEqual としてインスタンス化されたオブジェクト t1、t2、TestEqual としてインスタンス化された TestEqual t3、およびオブジェクトとしてインスタンス化されたオブジェクト o1 です。

プログラムを実行すると、これが出力になります。

0
1
2
3
In equals from TestEqual
4

この例は、通常の Car c = new Vehicle(); よりも少し複雑に見えます。c.drive(); メソッドを呼び出すオブジェクトはその型とは異なるインスタンスであり、メソッドのパラメーターもその型とは異なるインスタンスであるためです。

バインディングに関して、各メソッドを段階的に呼び出したときに何が起こるかを正しく理解しているかどうかを確認したいと思います。

 show 0
 t1.equals(t2)
 show 1

t1 は TestEqual オブジェクトと見なされます。メソッド equals はオーバーロードされているため、バインディングは静的です。これは、t2 をオブジェクトとして渡すことを意味し、オブジェクト スーパークラスから継承された equals メソッドを呼び出すため、テキストは表示されません。

 show 1
 t1.equals(t3)
 show 2

これは少し奇妙に思えます。t3 は TestEqual オブジェクトであるため、「In equals from TestEqual」と表示されることを期待していたので、t1 の equals を呼び出す必要があります。ここでの私の説明は、t1 は静的にバインドされ、オブジェクトと見なされるため、Object クラスから継承されたメソッド equals が呼び出され、パラメーター TestEqual t3 が Object にアップキャストされるということです。しかし、これは t1.equals(t2) からの前の説明が間違っているということではないでしょうか?

show 2
t3.equals(o1);
show 3

t3 は TestEqual オブジェクトで、パラメーター o1 は Object であるため、Object から継承された equals メソッドが呼び出され、何も出力されません。

show 3
t3.equals(t3)
show 4

t3 は TestEqual オブジェクトで、パラメーターは TestEqual オブジェクトであるため、TestEqual からオーバーロードされたメソッドが呼び出され、「In equals from TestEqual」が出力されます。

show 4
t3.equals(t2)

t3 は TestEqual オブジェクトであり、パラメーターは静的バインディング (メソッドのオーバーロード) により Object であるため、Object から継承された equal メソッドが呼び出され、何も出力されません。

4

2 に答える 2

3

メソッドObject.equals(Object obj)は、別のObjectインスタンスをパラメーターとして受け取ります。あなたが次のように定義した場合TestEqual

class TestEqual{
    @override
    public boolean equals(Object other ) {
        System.out.println( "In equals from TestEqual" ); return false;
    }
}

期待どおりに動作します。

于 2013-12-01T14:18:52.803 に答える
3

これは少し奇妙に思えます。t3 は TestEqual オブジェクトであるため、「In equals from TestEqual」と表示されることを期待していたので、t1 の equals を呼び出す必要があります。ここでの私の説明は、t1 は静的にバインドされ、オブジェクトと見なされるため、Object クラスから継承されたメソッド equals が呼び出され、パラメーター TestEqual t3 が Object にアップキャストされるということです。しかし、これは t1.equals(t2) からの前の説明が間違っているということではないでしょうか?

オーバーロードのコンテキストでメソッドを呼び出すには、最も具体的なメソッド呼び出しが発生し、コンパイル時に決定されます。最も具体的なメソッドを選択するルールは、Java 言語仕様 15.12.2.5 で定義されています。最も具体的な方法の選択: 他の議論では、言及されたステートメントは次のとおりです。

m1 が m2 よりも具体的で、m2 が m1 よりも具体的でない場合にのみ、メソッド m1 は別のメソッド m2 より厳密に具体的です。

ただし、コンテキストを説明するために、2 つの単純な Super クラスと sup クラスを宣言しましょう。

class SuperA
{
    public void test(SuperA a)
    {
        System.out.println("super class's test() is called");
    }
}

class SubB extends SuperA
{

    public void test(SubB subB)
    {
        System.out.println("subclass's test() is called");


    }    
}

ここで、この方法で 2 つのインスタンスを作成すると:

SuperA obj = new SubB();
SubB obj2 = new SubB();
obj.test(obj2);

test()コンパイル時により具体的であると判断され、コンパイラはそれobjが type のインスタンスであると判断するため、スーパークラスが呼び出されることがわかりますSuperAobjtoをキャストしてSuubB呼び出すtest(obj2):

((SubB)obj).test(obj2); // cast to SubB

そして、次のように出力されます:のメソッドを"subclass's test() is called"呼び出すことを暗示しています。これは、今回は、 のタイプと呼び出しの最も具体的な解決策があることをコンパイラが認識しているためです。test(obj)SubBobjSubBtest

ただし、次のように 2 つのインスタンスを宣言します。

   SuperA obj = new SubB();
   SuperA obj2 = new SubB();
   obj.test(obj2); // invokes super class's test method
   ((SubB)obj).test(obj2);// invokes super class's test method
   ((SubB)obj).test((SubB)obj2); // invoke sub class's test method

この一連の呼び出しでは、最初の 2 つのステートメントがスーパー クラスSuperAの test メソッドを呼び出します。これらの呼び出しはより具体的だからです。ただし、2番目のケースを説明するには:

((SubB)obj).test(obj2);// invokes super class's test method

今回、コンパイラobjは が の型を持っていることを認識していますが、メソッド呼び出しに対してより具体的なの型を持ってSubBいることをまだ認識しています。したがって、 と の両方が type で宣言されている 2 番目の例では、' Sメソッドを呼び出すためにキャストするために両方が必要になります。obj2SperAtestobjobj2SuperASubBSubBtest

ObjectJava のすべてのクラスのスーパー クラスと同様equalに、コンテキストのメソッド呼び出しシステムを理解する必要があります。この種の呼び出しの落とし穴を回避するためにequal、Java のクラスに実装されているすべてのメソッドが、実際にはメソッドクラスのオーバーライドであり 、チェックを利用していることがわかります。たとえば、クラスの equal メソッドの実装は次のとおりです。equalObjectinstanceofInteger

public boolean equals(Object obj) {
        if (obj instanceof Integer) { //<<<---- instance of checking
            return value == ((Integer)obj).intValue();
        }
        return false;
    } 
于 2013-12-01T15:22:54.020 に答える