オーバーライド“ equals”方法:パラメータのタイプを把握する方法
-
06-07-2019 - |
質問
パラメータ化されたクラスの equals
メソッドをオーバーライドしようとしています。
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Tuple))
return false;
Tuple<E> other = (Tuple<E>) obj; //unchecked cast
if (!a0.equals(other.a0) && !a0.equals(other.a1)) {
return false;
}
if (!a1.equals(other.a1) && !a1.equals(other.a0)) {
return false;
}
return true;
}
other
オブジェクトの&lt; E&gt;
が this
と同じであることを確認するにはどうすればよいですか?
解決
Class&lt; E&gt;
タイプへの参照を保持することにより、それを行うことができます。ただし、私の意見では、平等テストは、値が表現される具体的なタイプではなく、オブジェクトが表す値に関するものでなければなりません。。
この典型的な例は、たとえばコレクションAPIです。 new ArrayList&lt; String&gt;()。equals(new LinkedList&lt; Object&gt;())
は true
を返します。これらは完全に異なるタイプですが、「空のコレクション」という同じ値を表します。
個人的に、同じデータを表す2つの Tuple
(たとえば、(&quot; a&quot ;,&quot; b&quot;)
)は、1つが Tuple&lt; String&gt;
と入力し、もう一方は Tuple&lt; Object&gt;
?
他のヒント
消去のため、できません。できる最善の方法は、タプルが&quot; java.lang.Class&quot;に保持する予定のタイプをタプルクラスに格納することです。フィールドメンバー。次に、これらのフィールドを比較して、タプルクラスが同じ型を保持していることを確認できます。
このスレッドもご覧ください: C ++ Pair&lt; Lと同等のもの、R&gt; Javaの場合
クラスに関する詳細を投稿すると役立ちます。未チェックのキャストと同じフィールドの数は、Tuple&lt; E、F&gt;いいえ?
編集:これは、私が定期的に使用する便利なPairクラスです(必要に応じてTupleクラスを調整できます)。他の人による提案と同様に、このクラスは含まれるメンバーに平等の質問を決定させるだけです。ユースケースは、含まれているメンバーのタイプに本当に等しいかどうかを判断するものです。
/**
* Adapted from http://forums.sun.com/thread.jspa?threadID=5132045
*
*
* @author Tim Harsch
*
* @param <L>
* @param <R>
*/
public class Pair<L, R> {
private final L left;
private final R right;
public R getRight() {
return right;
} // end getter
public L getLeft() {
return left;
} // end getter
public Pair(final L left, final R right) {
this.left = left;
this.right = right;
} // end constructor
public static <A, B> Pair<A, B> create(A left, B right) {
return new Pair<A, B>(left, right);
} // end factory method
@Override
public final boolean equals(Object o) {
if (!(o instanceof Pair<?,?>))
return false;
final Pair<?, ?> other = (Pair<?, ?>) o;
return equal(getLeft(), other.getLeft()) && equal(getRight(), other.getRight());
} // end method
public static final boolean equal(Object o1, Object o2) {
if (o1 == null) {
return o2 == null;
}
return o1.equals(o2);
} // end method
@Override
public int hashCode() {
int hLeft = getLeft() == null ? 0 : getLeft().hashCode();
int hRight = getRight() == null ? 0 : getRight().hashCode();
return hLeft + (37 * hRight);
} // end method
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('<');
if( left == null ) {
sb.append("null");
} else {
sb.append(left.toString());
} // end if
sb.append(',');
if( right == null ) {
sb.append("null");
} else {
sb.append(right.toString());
} // end if
sb.append('>');
return sb.toString();
} // end method
} // end class
自分でこの問題に遭遇しましたが、特に特定の場合、タイプEを知る必要はありませんでした。
例:
public class Example<E> {
E value;
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Example<?> other = (Example<?>) obj;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
}
上記のコードでは、 Example&lt;?&gt;
を使用しているため、未チェックのキャストはありません。型パラメーターのワイルドカード「?」日を節約します。
残念ながら、コンパイル時にこれを行うことはできません。情報はなくなりました。これは、 型消去の結果です。 。別の方法は、パラメーターを Class
インスタンスとして保存し、後で検索することです。
ジェネリックはコンパイル時に消去されるため、基本的にできません。実行時に、すべての型パラメーターがなくなり、JVMに関する限り、それらはすべての点でまったく同じです。
それを回避する方法は、型を表す Class
フィールドを保存し、コンストラクタでその型を使用してオブジェクトを作成することです。
サンプルコンストラクター:
public class Tuple < E > {
public Tuple(Class<E> c) {
//store the class
}
}
または、ファクトリを使用できます:
public static <E> Tuple <E> getTuple(Class<E> type) {
// Create and return the tuple,
// just store that type variable in it
// for future use in equals.
}
Class
オブジェクトで E
の型への参照を保持する提案は、非効率的(それは多くの無意味な参照です) Class
はメモリを消費します)、問題には意味がありません。
Foo&lt; Bar&gt;
と Foo&lt; Baz&gt;
が等しくないことは一般的に真実ではありません。したがって、 E
は必要ありません。そして、あなたが書いたコードでは、コンパイルする必要さえありません。 Tuple&lt;?&gt;
にキャストするだけです。その時点で Tuple
について知っているのはそれだけだからです。それはまだコンパイルし、すべて。
2つの大きく異なるタイプのデータを持つ Tuple
が渡された場合、それらの要素は equals
ではなく、メソッドは false
、必要に応じて。 (希望は1つです。 equals
を正しく実装しているタイプに依存します。)
オフトピック-実装によれば、Tuple(a0、a1)はTuple(a1、a1)と等しいことを理解していますか?私はそれがあなたが望むものではないと思う...
話題では、他の人が言ったように、消去はこれを不可能にします。しかし、なぜこれが必要なのかを再検討する必要があります-等価チェックは実行時にのみ行われ、ジェネリックはコンパイル時のみです。概念的には、変数には汎用パラメーターがありますが、オブジェクトにはありません。したがって、オブジェクトの等価性を比較する場合、汎用パラメーターはまったく関係ありません。とにかく、実行時にそれらに基づいて適切なアクションを実行することはできません。
オブジェクトの等価性、およびジェネリックパラメーターの共分散/反分散は、2つの直交する問題です。
上記のコメントに同意します。クラスEを等しくする必要があるのはなぜですか? Eのサブクラスをどのように扱いますか?
とにかく、ここに役立つサンプルコードがあることを考えると:
public class Example<T> {
T t;
public Example(T t) {
this.t = t;
}
public static void main(String[] args) {
final String s = "string";
final Integer i = 1;
final Number n = 1;
final Example<String> exampleString = new Example<String>(s);
final Example<Integer> exampleInteger = new Example<Integer>(i);
final Example<Number> exampleNumber = new Example<Number>(n);
System.out.println("exampleString subclass " + exampleString.t.getClass());
System.out.println("exmapleIntger subclass " + exampleInteger.t.getClass());
System.out.println("exmapleNumber subclass " + exampleNumber.t.getClass());
System.out.println("Integer equals Number = " +
exampleInteger.t.equals(exampleNumber.t));
}
}
t.getClass()を呼び出して、Tの型に関するクラス情報を取得できます(もちろん、nullでないと仮定します)。
これが役立つことを願っています。
リフレクションを使用します。 http://tutorials.jenkov.com/java-reflection/generics.html