覆盖“等于”方法:如何确定参数的类型?
我正在尝试为参数化类重写equals
方法。
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof Tuple)) return false; Tuple other = (Tuple) 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
对象的与此相同?
您可以通过保留对Class
类型的引用来完成此操作。 但是,在我看来, 相等测试应该是对象所代表的值,而不是值表达的具体类型 。
一个典型的例子就是Collections API。 new ArrayList
返回true
。 虽然它们具有完全不同的类型,但它们代表相同的值,即“空集合”。
就个人而言,两个表示相同数据的Tuple
(例如("a", "b")
)是否不相等,因为一个是Tuple
类型而另一个是Tuple
?
因为擦除你不能。 关于你可以做的最好的事情是在元组类中存储你计划用于保存在“java.lang.Class”字段成员中的元组的类型。 然后你可以比较这些字段以确保元组类保持相同的类型。
另请参阅此主题: Java中的C ++ Pair
如果你发布更多关于你的课程,这将有所帮助。 我在考虑未经检查的演员表和你所等的字段数意味着它应该是元组
编辑:这是我经常使用的有用的Pair类(如果需要,你可以调整你的Tuple类)。 注意,类似于其他人的建议,这个类只是让包含的成员决定平等的问题。 您的用例应该确定相等性是否真正基于所包含成员的类型。
/** * Adapted from http://forums.sun.com/thread.jspa?threadID=5132045 * * * @author Tim Harsch * * @param * @param */ public class Pair { 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 Pair create(A left, B right) { return new Pair(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 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>
,因此没有未经检查的强制转换。 类型参数通配符’?’ 节省了一天。
可悲的是,你不能在编译时这样做; 信息消失了。 这就是类型擦除的后果。 另一种方法是将参数存储为Class
实例,然后再查找。
由于generics在编译时被删除 ,你基本上不能。 在运行时,任何类型参数都消失了,就JVM而言,它们在所有方面都完全相同。
解决这个问题的方法是存储一个表示类型的Class
字段,并在构造函数中创建具有该类型的对象。
示例构造函数:
public class Tuple < E > { public Tuple(Class c) { //store the class } }
或者你可以使用工厂:
public static Tuple getTuple(Class type) { // Create and return the tuple, // just store that type variable in it // for future use in equals. }
使用Class
对象保留对E
类型的引用的建议似乎效率低下 (这对于占用内存的Class
很多毫无意义的引用)并且对于您的问题毫无意义。
一般情况下, Foo
和Foo
应该是不相等的。 所以你不需要E
并且,在您编写的代码中,您甚至不需要它来编译。 简单地演绎到Tuple>
,因为那时你真正了解了Tuple
。 它仍然编译和所有。
如果你使用两种截然不同的类型的数据传递Tuple
s,那些元素将不是equals
,并且你的方法将根据需要返回false
。 (人们希望 – 取决于那些实现equals
类型。)
偏离主题 – 您是否意识到根据您的实现,元组(a0,a1)等于元组(a1,a1)? 我怀疑那不是你想要的……
正如其他人所说的那样,在主题上,擦除使得这是不可能的。 但是你应该重新考虑为什么要这样 – 平等检查只在运行时发生,而generics只是编译时 。 从概念上讲, 变量具有通用参数,但对象则不具有。 因此,当您比较对象的相等性时,通用参数根本不重要; 无论如何,你不能在运行时根据它们采取任何适当的行动。
对象相等和通用参数共同/反向方差是两个正交问题。
我同意上述评论,为什么E级需要平等? 以及如何处理E的子类?
无论如何,鉴于这是一个可能对您有帮助的代码示例:
public class Example { 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 exampleString = new Example (s); final Example exampleInteger = new Example (i); final Example exampleNumber = new Example (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)。
我希望这有帮助。