Java equals()的实现:与实现的接口或实现的类进行比较?
我一直想知道如何最好地为一系列实现相同接口的类实现equals()(客户机应该只使用所述接口,而不知道如何实现类) 我还没有编写自己的具体示例,但JDK中有两个示例-java.lang.Number和java.lang.CharSequence说明了这一决定:Java equals()的实现:与实现的接口或实现的类进行比较?,java,interface,equals,Java,Interface,Equals,我一直想知道如何最好地为一系列实现相同接口的类实现equals()(客户机应该只使用所述接口,而不知道如何实现类) 我还没有编写自己的具体示例,但JDK中有两个示例-java.lang.Number和java.lang.CharSequence说明了这一决定: boolean b1 = new Byte(0).equals( new Integer(0) ) ); 还是用CharSequence boolean b2 = "".equals(new StringBuilder()); 理想情
boolean b1 = new Byte(0).equals( new Integer(0) ) );
还是用CharSequence
boolean b2 = "".equals(new StringBuilder());
理想情况下,您是否希望这些评估结果为真?这两种类型都实现了相同的数据类型接口,作为一个处理数字(resp.CharSequences)实例的客户机,如果equals比较接口类型而不是实现类型,我的生活会更轻松
现在这不是一个理想的例子,因为JDK向公众公开了实现类型,但是假设我们不必维护与现有类型的兼容性——从设计师的角度来看:equals应该检查接口还是更好的方式,检查实现
注意:我理解,在实践中,根据接口检查相等性可能很难真正正确实现,而且由于相等的接口还需要返回相同的hashCode(),这使得检查更加棘手。 但这些只是实现中的障碍,例如CharSequence,尽管接口非常小,但平等性检查所需的一切都存在,而没有揭示实现的内部结构(因此基本上可以正确实现,即使事先不知道未来的实现).
但我更感兴趣的是设计方面,而不是如何实际实现它。我不会仅仅根据实现的难度来决定。我通常会假设“相似”对象不相等——例如,我不希望整数(1)传递等于(长(1))。我可以想象这样做是合理的,但是由于jdk需要是一个通用API,所以您无法假设这始终是正确的做法 如果你有一些合理的自定义对象,我认为如果你
- 确保没有一些边缘情况下确实需要更具体的相等性(即需要相同的类)
- 把它记录得非常清楚
- 确保hashcode与新的equals行为一致
实现
您的接口,定义final equals()/hashCode()方法,并让您的客户扩展它:
public interface Somethingable {
public void something();
}
public abstract class AbstractSomethingable implements Somethingable {
public final boolean equals(Object obj) {
// your consistent implementation
}
public final int hashCode() {
// your consistent implementation
}
}
请注意,通过将类抽象化
,可以实现接口
,而无需定义接口的方法
您的客户仍然必须实现something()
方法,但他们的所有实例都将使用您的代码来表示equals()/hashCode()(因为您已经将这些方法设置为final)
与您的客户不同的是:
- 使用
关键字而不是扩展
关键字(次要)实现
- 无法扩展他们选择的其他类以使用您的API(可能是次要的,也可能是主要的-如果可以接受,那么就去做)
在非常罕见的情况下,我可能会设计客户端允许注入比较器或-当不可用时-将我的数字封装成变量类型。
< P>我会考虑对没有相同的具体类型的两个对象返回真的是相等的,这是非常令人惊讶的行为。如果您在编译时知道接口的每一个可能实现者的盒子中操作,那么您可以构造仅使用接口方法就有意义的等式,但这对于API/框架代码来说是不现实的 当您调用用于实现equals的方法时,您甚至不能确定是否没有人会编写一个接口的实现来改变其内部状态!谈论混淆,一个返回true并在过程中使自身失效的equals检查 -- 这就是我所理解的“根据接口检查平等性”的问题:public interface Car {
int speedKMH();
String directionCardinal();
}
public class BoringCorrolla implements Car {
private int speed;
private String directionCardinal;
public int speedKMH() { return speed; }
public String directionCardinal() { return directionCardinal; }
@Override
public boolean equals(Object obj) {
if (obj isntanceof Car) {
Car other = (Car) obj;
return (other.speedKMH() == speedKMH() && other.directionCardinal().equals(directionCardinal());
}
}
}
public class CRAZYTAXI implements Car, RandomCar {
public int speedKMH() { return randomSpeed(); }
public String directionCardinal() { return randomDirection();}
}
我会尝试向我的接口添加另一个equals方法。那么:
assertFalse(new Integer(0).equals(new Byte(0))); // pass
assertTrue(new Integer(0).valueEquals(new Byte(0))); // hypothetical pass
这不会产生意外的行为(不同类型相同),但可以检查是否存在相同的值
在有效的java中有一个相关的主题,其中讨论了等于instanceof和getClass。但是,无法记住项目编号。可以定义不同类之间的相等性 在您的情况下,接口必须指定精确的相等算法,因此任何实现接口的类都必须遵守它。更好的是,由于该算法只依赖于inferface公开的信息,所以只需实现它,子类就可以简单地借用它
interface Foo
class Util
static int hashCode(Foo foo){ ... }
static boolean equal(Foo a, Foo b){ ... }
static boolean equal(Foo a, Object b)
return (b instanceof Foo) && equal(a, (Foo)b);
class FooX implements Foo
int hashCode()
return Util.hashCode(this);
boolean equals(Object that)
return Util.equal(this, that);
另外,确保equals是对称的:如果a.equals(b),那么b.equals(a),反之亦然。@JB Nizet:有时有用的一种模式是有两个equals方法;如果第一种方法既不能确定等式也不能确定不等式