压倒“等于”方法:如何弄清楚参数的类型?
-
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;
}
如何确保其他
对象的&lt; E&gt;
与 this
相同?
解决方案
您可以通过保留对 Class&lt; E&gt;
类型的引用来实现。但是,在我看来,等式测试应该是关于对象所代表的值,而不是表达值的具体类型。
典型的例子是Collections API。 new ArrayList&lt; String&gt;()。equals(new LinkedList&lt; Object&gt;())
返回 true
。虽然它们具有完全不同的类型,但它们代表相同的值,即“空集合”。
就个人而言,代表相同数据的两个 Tuple
应该是不相等的,因为其中一个是(“a”,“b”,
)输入 Tuple&lt; String&gt;
,而另一个是 Tuple&lt; Object&gt;
?
其他提示
因为擦除你不能。关于你可以做的最好的事情是在元组类中存储你为Tuple计划保存在“java.lang.Class”中的类型。现场成员。然后你可以比较这些字段以确保元组类保持相同的类型。
另见此主题: C ++对&lt; L的等价物是什么,R&GT;在Java?
如果你发表关于你班级的更多信息会有所帮助。我正在考虑未经检查的演员表和你所等的字段数意味着它应该是元组&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
类型的引用的建议似乎效率低下(这是对a的一些无意义的引用 Class
占用内存)并且对你的问题毫无意义。
Foo&lt; Bar&gt;
和 Foo&lt; Baz&gt;
应该是不相等的。所以你不需要 E
。并且,在您编写的代码中,您甚至不需要它来编译。只需转换为 Tuple&lt;?&gt;
,因为那时您真正了解 Tuple
。它仍然可以编译。
如果你传递了 Tuple
,其中包含两种截然不同的类型的数据,那些元素将不是 equals
,你的方法将返回 false
,根据需要。 (人们希望 - 取决于那些实现 equals
的类型。)
偏离主题 - 您是否意识到根据您的实现,元组(a0,a1)等于元组(a1,a1)?我怀疑那不是你想要的......
正如其他人所说的那样,在主题上,擦除使得这一切变得不可能。但是你应该重新考虑为什么要这样 - 平等检查只在运行时发生,泛型只是编译时。从概念上讲,变量具有通用参数,但对象不具有。因此,当您比较对象的相等性时,通用参数根本不重要;无论如何,你不能在运行时基于它们采取任何适当的行动。
对象相等和泛型参数co- / contra-variance是两个正交问题。
我同意上述评论,为什么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。)
我希望这会有所帮助。