Javaでequalsをオーバーライドする場合、Object以外のパラメーターを使用するとなぜ機能しないのですか?
-
08-07-2019 - |
質問
最近、興味深い動作に遭遇しました。 Object以外のパラメータを取るために.equals()をオーバーライドすると、呼び出されないようです。誰がこれが起こっているのか私に説明できますか? OOPのポリモーフィズムに対する私の理解を侵害しているようですが、何かが足りないかもしれません。
ここに、私が見ているものを示すはるかに単純なコードを示します。
public class MyClass {
private int x;
public MyClass(int n) { x = n; }
public boolean equals(Object o) { return false; }
public boolean equals(MyClass mc) { return x == mc.x; }
public static void main(String[] args) {
List<MyClass> list = new ArrayList<MyClass>();
list.add(new MyClass(3));
System.out.println("Contains 3? " + list.contains(new MyClass(3)));
}
}
これを実行すると、<!> quot; Contains 3? false
<!> quot;が出力されます。動作する別の関数がありますが、equals(Object)関数が呼び出されているようです。対照的に、このように等号を書くと、コードは期待どおりに動作します:
public boolean equals(Object o) {
if(!(o instanceof MyClass))
return false;
MyClass mc = (MyClass)o;
return x == mc.x;
}
パラメータのタイプに基づいて、どのバージョンの関数を呼び出すかを判断しないのはなぜですか?
解決
<!> quot;オーバーライド<!> quot;および<!> quot; overloading <!> quot;。
オーバーライド-ポリモーフィズムの目的で既存のメソッドの置換定義を追加します。メソッドには同じ署名が必要です。署名は、名前と引数のタイプで構成されます。オーバーライドされたメソッドは、ターゲットオブジェクトのランタイムタイプに基づいて実行時に選択されます。
オーバーロード-同じ名前で異なるシグネチャを持つメソッドを追加します。オーバーロードされたメソッドは、ターゲットオブジェクトのコンパイル時のタイプに基づいて、コンパイル時に選択されます。
他のヒント
equals(Object)はスーパーメソッドをオーバーライドしています。まったく同じシグニチャーを使用せずにスーパーメソッドをオーバーライドすることはできません( )。
呼び出しているメソッドがArrayList <E
<!> gtのjavadocで定義されていることに注意してください。 as
boolean contains(Object o)
Returns true if this list contains the specified element.
の代わりに
boolean contains(E o)
Returns true if this list contains the specified element.
ArrayList.javaの実装:
private transient Object elementData[];
public boolean contains(Object elem) {
return indexOf(elem) >= 0;
}
public int indexOf(Object elem) {
if (elem == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (elem.equals(elementData[i]))
return i;
}
return -1;
}
このメソッドはArrayList <=> <!> gt;の実装でオーバーライドされないため、Objectスーパークラスで定義されたequalsメソッドを使用します。
javaでObject equalsをオーバーライドする場合は、Object hashCodeメソッドもオーバーライドする必要があります。
とにかく、次のコードを試してみてください:
class A{
public int content;
A(){
this(0);
}
A(int value){
content = value;
}
public boolean equals(Object obj){
System.out.println("overriding equals method");
return this.content == ((A) obj).content;
}
public boolean equals(A a){
System.out.println("overloading equals method");
return this.content == a.content;
}
public static void main(String[] args){
A x = new A(1);
A y = new A(2);
Object z = new A(1);
System.out.println(x.equals(y));
System.out.println(x.equals(x));
System.out.println(x.equals(z));
//override as z is declared as Object at compile time
//so it will use methods in class Object instead of class A
System.out.println(x.equals((Object) y));
System.out.println(x.equals((Object) x));
}
}
//rant: they didn't teach me these in javaschool and I had to learn it the hard way.
contains(Object)メソッドのArrayList実装は、内部的にObject.equals(Object)メソッドを使用するようにバインドされているため、equals(MyClass)メソッドのオーバーロードについて知ることはありません。 (一致するシグネチャを持つ)オーバーライドメソッドのみが見つかります。
わかりました、言い直してください。
(1)コンパイラはジェネリックに関するすべての情報を削除するため(消去、こちら)、および(2)まったく同じ署名(equals(Object))を持たないメソッドをオーバーライドできないため、(3)実行時にリスト内のすべてのオブジェクトはインスタンスとしてではなくオブジェクトとして扱われますMyClassの。したがって、呼び出されるメソッドはequals(Object)です。これはクラスによって上書きされるためです。
contains()
のList
メソッドは、実行時にオブジェクトのタイプを知っていると仮定していますが、これは正しくありません。
消去のため、List<MyClass>
は実行時に通常のObject
になるため、equals()
メソッドはそのパラメーターをMyClass
と見なし、<=に対して定義したものの代わりにオブジェクトの<=>を呼び出します>実行中。