Java – 基类和子类中的equals方法
我有一个简单的基类,后来由许多单独的类扩展,这可能会引入新的字段,但不一定。 我在基类中定义了一个equals方法,但是也为一些子类重写了它。 可以在基础/子类中混合定义吗? 在我的情况下,它是为了避免代码重复检查相同的字段。
看看Angelika Langer的“实现等于()允许混合型比较” 。
以下是一些问题和可能的解决方案的简要说明:
等于合同说(其中包括):
它是对称的:对于任何非空引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)才应返回true。
这意味着如果您的子类引入了新字段并且您正在将基类的对象(或不覆盖equals的另一个子类)与此子类的对象进行比较,则可能会遇到问题。
请勿执行以下操作:
class BaseClass { private int field1 = 0; @Override public boolean equals(Object obj) { if (obj instanceof BaseClass) { return field1 == ((BaseClass) obj).field1; } return false; } } class BadSubClass extends BaseClass { private int field2 = 0; @Override public boolean equals(Object obj) { if (obj instanceof BadSubClass) { return super.equals(obj) && field2 == ((BadSubClass) obj).field2; } return false; } }
因为你得到了
BaseClass baseClass = new BaseClass(); BadSubClass subClass = new BadSubClass(); System.out.println(baseClass.equals(subClass)); // prints 'true' System.out.println(subClass.equals(baseClass)); // prints 'false'
可能的解决方案:
用类比较替换instanceof
-check:
obj != null && obj.getClass() == getClass()
使用此解决方案, BaseClass
的对象永远不会等于任何子类的对象。
如果你创建另一个没有equals
方法的@Override的SubClass
,那么两个SubClass
-objects可以相互相等(如果BaseClass.equals
检查决定这样)开箱即用,但SubClass
-object永远不会等于a BaseClass
-object。
一个好的实施可能如下:
class BaseClass { private int field1 = 0; @Override public boolean equals(Object obj) { if (obj != null && obj.getClass() == getClass()) { return field1 == ((BaseClass) obj).field1; } return false; } } class GoodSubClass extends BaseClass { private int field2 = 0; @Override public boolean equals(Object obj) { if (obj instanceof GoodSubClass) { return super.equals(obj) && field2 == ((GoodSubClass) obj).field2; } return false; } }
有关更高级的问题及其解决方案,请参阅上面提到的文章。
不,在引入与equals方法相关的新字段时,不可能符合equals合同。 有关更多信息,请参阅Joshua Bloch的“Effective Java”。
编辑:
我现在手头没有这本书,但我认为如果基类是抽象的/无法实例化的话就没问题。
我认为只要遵循eqauls()和hashcode()契约就完全没问题了。
您可以使用super()
方法调用您正在扩展的类的方法,以防止任何代码重复的需要
public class BaseClass { public boolean equals(BaseClass other) { return (other.getBlahblah() == this.Blahblah && .....); } } public class DerivedClass extends BaseClass { public boolean equals(DerivedClass other) { return (super(other) && other.getNewAttribute() == this.NewAttribute.....); } }
相当有效的方法。 问题出在你的一个子类中,它必须保留equals的定义,因为它的父类绑定了。 否则你有一个破坏的equals函数,这可能会在运行时导致一些非常独特的场景。
我想,那就像Java
那样在super
class
提供equals(Object obj)
和hashCode()
方法实现是完美的。 我们都知道Java在基类java.lang.Object中提供了hashCode() and equals(Object obj)
方法实现,并且在需要时我们在class
override
它们。
虽然下面没有处理所有情况,但我发现它非常实用。 当我同时使用SuperClass和SubClass时,我已经多次使用过这个。 我不想比较它们,但我也不想为SubClass重新实现所有SuperClass equals()。 它处理:
- a.equals(b)== b.equals(a)
- 不重复字段比较代码
- 轻松推广任何子类深度
- Subclass.equals(SuperClass)== false
- Superclass.equals(SubClass)== false
代码示例
// implement both strict and asymmetric equality class SuperClass { public int f1; public boolean looseEquals(Object o) { if (!(o instanceof SuperClass)) return false; SuperClass other = (SuperClass)o; return f1 == other.f1; } @Override public boolean equals(Object o) { return looseEquals(o) && this.getClass() == o.getClass(); } } class SubClass extends SuperClass { public int f2; @Override public boolean looseEquals(Object o) { if (!super.looseEquals(o)) return false; if (!(o instanceof SubClass)) return false; SubClass other = (SubClass)o; return f2 == other.f2; } // no need to override equals() }