这个问题具体涉及各种实施方案的性能和某种程度的简洁性。
我用this article对实现平等权利进行了更新。我的问题特别对应canEqual
(以确保等价关系)。
而不是重载canEquals方法以在层次结构中的每个类中使用instanceOf(paramenter的实例是编译时类)。为什么不在顶级类中使用isAssignableFrom(动态解析)。使得更简洁的代码,你不必重载第三种方法。
虽然这个替代方案有效。我需要注意哪些性能方面的考虑因素?
enum Color {
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET;
}
class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override public boolean equals(Object other) {
boolean result = false;
if (other instanceof Point) {
Point that = (Point) other;
//Option 1
//result = (that.canEqual(this) && this.getX() == that.getX() && this.getY() == that.getY());
//Option 2
//result = (that.getClass().isAssignableFrom(this.getClass()) && this.getX() == that.getX() && this.getY() == that.getY());
//Option 3
//result = (getClass() == that.getClass() && this.getX() == that.getX() && this.getY() == that.getY());
}
return result;
}
@Override public int hashCode() {
return (41 * (41 + x) + y);
}
public boolean canEqual(Object other) { return (other instanceof Point); }
}
public class ColoredPoint extends Point{
Color color;
public ColoredPoint(int x, int y, Color color) {
super(x, y);
this.color = color;
}
@Override public boolean equals(Object other) {
boolean result = false;
if (other instanceof ColoredPoint) {
ColoredPoint that = (ColoredPoint) other;
result = (this.color.equals(that.color) && super.equals(that));
}
return result;
}
@Override public int hashCode() {
return (41 * super.hashCode() + color.hashCode());
}
@Override public boolean canEqual(Object other) { return (other instanceof ColoredPoint); }
public static void main(String[] args) {
Object p = new Point(1, 2);
Object cp = new ColoredPoint(1, 2, Color.INDIGO);
Point pAnon = new Point(1, 1) {
@Override public int getY() {
return 2;
}
};
Set<Point> coll = new java.util.HashSet<Point>();
coll.add((Point)p);
System.out.println(coll.contains(p)); // prints true
System.out.println(coll.contains(cp)); // prints false
System.out.println(coll.contains(pAnon)); // prints true
}
}
答案 0 :(得分:4)
更新:实际上,您的方法在技术上并不像我首先想到的那样有效,因为它违反了equals
对于不覆盖equals
的子类的对称性约定:
Point p = new Point(1, 2);
Point pAnon = new Point(1, 1) {
@Override public int getY() {
return 2;
}
};
System.out.println(p.equals(pAnon)); // prints false
System.out.println(pAnon.equals(p)); // prints true
原因是p.getClass().isAssignableFrom(pAnon.getClass())
为true
,反之,pAnon.getClass().isAssignableFrom(p.getClass())
为false
。
如果您不相信这一点,请尝试实际运行您的代码并将其与文章中的版本进行比较:您会注意到它打印true, false, false
,而不是true, false, true
,如同制品
答案 1 :(得分:3)
除非你想允许比较不同类型的类,否则最简单,最安全,最简洁,最有效的方法是:
(getClass() == that.getClass())
答案 2 :(得分:1)
my answer见What is the difference between equality and equivalence?。
您不能将来自不同类的两个对象等同起来,因为它会破坏对称性。
修改强>:
归结为以下x
:
if (other instanceof Point) {
Point that = (Point) other;
boolean x = that.getClass().isAssignableFrom(this.getClass());
}
与getClass() == that.getClass()
具有相同的权力。
根据{{3}}的答案,它没有。
即使它是正确的,我也不会通过调用that.getClass().isAssignableFrom
来看到任何性能优势。
答案 3 :(得分:1)
到目前为止给出的所有答案都没有回答这个问题 - 但指出了equals()
合同。等式必须是等价关系(传递,对称,自反),并且相等的对象必须具有相同的哈希码。这完全适用于子类 - 提供的子类本身不会覆盖equals()
或hashCode()
。所以你有两个选择 - 要么从equals()
继承Point
(所以如果ColoredPoint
实例具有相同的坐标,即使它们具有不同的颜色,它们是相等的),或者你覆盖{ {1}}(现在必须确保equals()
和Point
永远不相等。
如果您需要执行逐点比较,请不要使用ColoredPoint
- 请改为编写方法equals()
。
无论您选择做什么,您仍然需要在pointwiseEquals()
中执行课程检查。
equals()
显然是表现最好的,但是如果你希望能够通过不会覆盖getClass() == that.getClass()
的相等测试子类,它确实会破坏(实际上,你可以保证唯一的方法就是class或者等式final,并且根本不允许任何子类重写。如果它是equals()
和instanceOf
之间的选择,则没有实际差异,它们实际上都执行相同的运行时测试(唯一的区别是,isAssignableFrom
可以执行编译时健全性检查,但在这种情况下,当输入只是instanceOf
时,它无法知道任何事情。在这两种情况下,运行时检查都是相同的 - 检查对象列出的接口中的目标类(这里不适用,因为我们没有检查接口),或者在类层次结构中向上走,直到找到列出的课程或到达根目录。
答案 4 :(得分:1)
这是我对澄清问题的第二个答案
考虑我们何时致电Point.equals(ColoredPoint cp);
Point.equals()首先检查
if (other instanceof Point)...
哪个过去了。在提出的三个选项中,所有三个选项都检查另一个对象(在本例中为ColoredPoint)是否满足更多测试。选项包括:
从性能(和设计)的角度来看,检查other instanceof Point
没有价值,因为OP想要的实际行为(他一直无法表达)是针对他的特定用例,这些对象意味着它们必须是同一个类。
因此,对于性能和设计,只需使用
this.getClass() == that.getClass()
正如@jthalborn所建议的
当后来的编码人员在你的代码中看到instanceof或isAssignableFrom时,他会认为允许子类等于基类,这完全是误导。
答案 5 :(得分:0)
我认为你的解决方案会失败,因为它不是传递OOPS,是对称的。 See The chapter from Effective Java
Point p = new Point(2,3);
ColoredPoint cp = new ColoredPoint(2,3, Color.WHITE);
我相信(没有运行你的代码)
p.equals(cp)为真
但
cp.equals(p)为假
虽然我不完全理解你的代码 - 它指的是被注释掉的canEquals()。简短的回答是,你要么必须忽视颜色的平等,要么你必须做@jthalborn所建议的。
答案 6 :(得分:0)
好的,我们这里有Effective Java的例子(我有2008年第2版)。
该示例位于ITEM 8: OBEY THE GENERAL CONTRACT WHEN OVERRIDING EQUALS
从第37页开始(我写这个以防您想要检查)。
class ColoredPoint extends Point{}
并且有2个参与者在演示为什么instanceof是BAD。第一次尝试是
// Broken - violates symmetry!
@Override public boolean equals(Object o) {
if (!(o instanceof ColorPoint))
return false;
return super.equals(o) && ((ColorPoint) o).color == color;
}
,第二个是
// Broken - violates transitivity!
@Override public boolean equals(Object o) {
if (!(o instanceof Point))
return false;
// If o is a normal Point, do a color-blind comparison
if (!(o instanceof ColorPoint))
return o.equals(this);
// o is a ColorPoint; do a full comparison
return super.equals(o) && ((ColorPoint)o).color == color;
}
首先,永远不会达到第二个IF。如果'o'不是作为ColorPoint超类的Point,那么如何将非Point作为ColorPoint ??????
所以从一开始的第二次尝试是错误的!真正比较的唯一机会是super.equals(o) && ((ColorPoint)o).color == color;
,这还不够!
这里的解决方案是:
if (super.equals(o)) return true;
if (!(o instanceof ColorPoint))
if ((o instanceof Point)) return this.equals(o);
else return false;
return (color ==((ColorPoint)o).color && this.equals(o));
obj.getClass()用于非常特定的equals(),但您的实现取决于您的范围。你如何定义两个对象是否相等?实施它,它将相应地工作。