java 覆盖“equals”方法:如何确定参数的类型?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1628718/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Overriding "equals" method: how to figure out the type of the parameter?
提问by Nick Heiner
I'm trying to override equalsmethod for a parameterized class.
我正在尝试覆盖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;
}
How can I make sure that <E>of the otherobject is the same as this?
我怎样才能确保<E>该的other对象是一样的this?
采纳答案by notnoop
You can do it by retaining a reference to Class<E>type. However, in my opinion, equality tests should be about the values the objects represent rather than the concrete types the values get expressed.
您可以通过保留对Class<E>类型的引用来实现。然而,在我看来,相等性测试应该是关于对象所代表的值而不是值所表达的具体类型。
A classic example of this is the Collections API for example. new ArrayList<String>().equals(new LinkedList<Object>())returns true. While these have completely different types, they represent the same value, namely "an empty collection".
一个典型的例子是 Collections API。 new ArrayList<String>().equals(new LinkedList<Object>())返回true。虽然它们具有完全不同的类型,但它们代表相同的值,即“一个空集合”。
Personally, should two Tuples that represent the same data (e.g. ("a", "b")) be not equal, because one is of type Tuple<String>while the other is Tuple<Object>?
就个人而言,Tuple代表相同数据(例如("a", "b"))的两个s 是否应该不相等,因为一个是类型Tuple<String>而另一个是类型Tuple<Object>?
回答by harschware
Because of erasure you can't. About the best you could do is store in the tuple class the type you plan for the Tuple to hold in a "java.lang.Class" field member. Then you could compare those fields to make sure the tuple class is holding the same types.
因为擦除你不能。关于您能做的最好的事情是将您计划在“java.lang.Class”字段成员中保存的元组类型存储在元组类中。然后您可以比较这些字段以确保元组类拥有相同的类型。
Also see this thread: What is the equivalent of the C++ Pair<L,R> in Java?
另请参阅此线程: Java 中 C++ Pair<L,R> 的等价物是什么?
It would help if you post more about your class. I'm thinking the unchecked cast and your number of fields you equate means it should be Tuple<E,F> no?
如果您发布更多关于您的课程的信息会有所帮助。我在想未经检查的演员表和你等同的字段数意味着它应该是 Tuple<E,F> 否?
EDIT: here is a useful Pair class I use regularly (you can adapt your Tuple class if needed). Note, similiar to suggestions by others this class just lets the contained members decide the question of equality. Your use case is what should determine whether equality is really based on the type of the contained members.
编辑:这是我经常使用的一个有用的 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
回答by jfritz42
I just ran into this problem myself, and in my -particular- case, I didn't need to know the type E.
我自己刚刚遇到了这个问题,在我的特殊情况下,我不需要知道类型 E。
For example:
例如:
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;
}
}
In the above code, there is no unchecked cast because of using Example<?>. The type parameter wildcard '?' saves the day.
在上面的代码中,没有因为使用了未经检查的强制转换Example<?>。类型参数通配符“?” 节省了一天。
回答by Yishai
Since generics are erased at compile time, you basically can't. At runtime, any type parameter is gone, and as far as the JVM is concerned, they are exactly the same in all respects.
由于泛型在编译时被删除,你基本上不能。在运行时,任何类型参数都没有了,就 JVM 而言,它们在各方面都完全相同。
The way to work around that is to store a Classfield that represents the type, and create the object with that type in the constructor.
解决这个问题的方法是存储一个Class表示类型的字段,并在构造函数中创建具有该类型的对象。
A sample constructor:
示例构造函数:
public class Tuple < E > {
public Tuple(Class<E> c) {
//store the class
}
}
Or you could use a factory:
或者你可以使用工厂:
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.
}
回答by John Feminella
Sadly, you can't do this at compile time; the information is gone. Such are the consequences of type erasure. An alternative is to store the parameter as a Classinstance, and then look it up later.
遗憾的是,您不能在编译时执行此操作;信息没了。这就是类型擦除的后果。另一种方法是将参数存储为Class实例,然后稍后再查找。
回答by Sean Owen
The suggestions to retain a reference to E's type with a Classobject seem inefficient(that's a lot of pointless references to a Classtaking up memory) and pointless for your problem.
E使用Class对象保留对's 类型的引用的建议似乎效率低下(这是对Class占用内存的大量无意义引用)并且对您的问题毫无意义。
It's not generally true that Foo<Bar>andFoo<Baz>should be unequal. So you don't need E. And, in the code you wrote, you don't even need it to compile. Simply cast to Tuple<?>, since that is truly all you know about Tupleat that point. It still compiles and all.
这不是通常正确的Foo<Bar>,并Foo<Baz>应该是不平等的。所以你不需要E. 而且,在您编写的代码中,您甚至不需要编译它。只需强制转换为Tuple<?>,因为这就是您当时真正了解的全部内容Tuple。它仍然编译和所有。
If you are passed Tuples with data of two wildly different types, those elements will not be equals, and your method will return false, as desired. (One hopes -- depends on those types implementing equalssanely.)
如果您通过Tuple两种截然不同类型的数据传递s,则这些元素将不会是equals,并且您的方法将false根据需要返回。(一个希望——取决于那些equals理智地实施的类型。)
回答by Andrzej Doyle
Off-topic - do you realise that according to your implementation, Tuple(a0, a1) is equal to Tuple(a1, a1)? I suspect that's not what you want...
题外话 - 您是否意识到根据您的实现,Tuple(a0, a1) 等于 Tuple(a1, a1)?我怀疑这不是你想要的......
On-topic, as others have said, erasure makes this impossible. But you should reconsider why you want this - equality checking only happens at runtime, and generics are only compile-time. Conceptually, a variablehas generic parameters, but an objectdoes not. Thus when you're comparing the equality of objects, the generic parameters do not matter at all; you can't take any appropriate action based on them at runtime anyway.
正如其他人所说,在主题上,擦除使这成为不可能。但是您应该重新考虑为什么要这样做 - 相等性检查仅在运行时发生,而泛型仅在编译时发生。从概念上讲,变量具有通用参数,但对象没有。因此,当您比较对象的相等性时,泛型参数根本无关紧要;无论如何,您无法在运行时根据它们采取任何适当的操作。
Object equality, and generic parameter co-/contra-variance, are two orthogonal concerns.
对象相等性和泛型参数协方差/逆方差是两个正交的关注点。
回答by MrJacqes
I agree with the comments above, why does the class E need to be equal? and how do you want to treat subclasses of E?
我同意上面的评论,为什么 E 类需要相等?你想如何处理 E 的子类?
Anyway, given that here is a code sample that may help you:
无论如何,鉴于这是一个可以帮助您的代码示例:
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));
}
}
You can call t.getClass() to get class information about the type of T (assuming it is not null, of course.)
您可以调用 t.getClass() 来获取有关 T 类型的类信息(当然,假设它不为空。)
I hope this helps.
我希望这有帮助。

