我一直想知道如何最好地为一系列类实现equals(),这些类都实现了相同的接口(并且客户端应该只与所述接口一起工作,从不知道实现类)。
我还没有编写自己的具体示例,但JDK中有两个例子 - java.lang.Number和java.lang.CharSequence来说明决定:
boolean b1 = new Byte(0).equals( new Integer(0) ) );
或使用CharSequence
boolean b2 = "".equals(new StringBuilder());
您是否希望那些评估为真或不合适?这两种类型都实现了相同的数据类型接口,并且作为使用Numbers(resp.CharSequences)实例的客户端,如果equals将比较接口类型而不是实现类型,我将有更轻松的生活。
现在这不是一个理想的例子,因为JDK向公众公开了实现类型,但是假设我们没有必须维护已经存在的内容 - 从设计者的角度来看:应该等于对接口的检查,或者它是否更好,检查实现?
注意:我知道检查接口的相等性可能非常难在实践中实际正确实现,甚至更棘手,因为相同的接口也需要返回相同的hashCode()。 但这些只是实现中的障碍,例如CharSequence,虽然接口非常小,但是相等检查所需的一切都没有透露实现的内部结构(所以它主要可以正确实现,甚至没有提前了解未来的实施)。 但我对设计方面更感兴趣,而不是如何实际实现它。我不会仅根据实施的难度来决定。
答案 0 :(得分:5)
定义一个抽象类implements
你的接口并定义最终的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
}
}
请注意,通过创建类abstract
,您可以implements
界面而无需定义界面的方法。
您的客户仍然需要实施something()
方法,但他们的所有实例都会使用您的代码来实现equals()/ hashCode()(因为您已经制作了这些方法{{ 1}})。
与客户的不同之处在于:
final
关键字代替extends
关键字(次要)答案 1 :(得分:3)
我通常会认为“相似”的对象不会相等 - 例如我不希望Integer(1)会传递equals(Long(1))。我可以想象那些合情合理的情况,但由于jdk需要是一个通用的API,你不能假设总是是正确的事情。
如果你有一些合理的自定义对象,我认为如果你
那么实现一个扩展的equals定义是完全没问题的。答案 2 :(得分:0)
对于它的价值,我可能会做一个特定于实现的equals实现(旁注 - 不要忘记实现hashCode ......)。接口级equals()给接口的实现者带来了沉重的负担 - 他们可能会也可能不会意识到特殊要求。
通常,实现级别工作正常,因为您的客户端只处理一个实现(即MyNumberProcessor 可以对任何数字起作用,但实际上它的一个实例只需要处理Long,也许只能处理另一个双)。泛型是确保发生这种情况的好方法。
在极少数情况下它很重要,我可能会设计客户端以允许注入比较器,或者 - 如果不可用 - 将我的Numbers封装到VarTypeNumber中。
答案 3 :(得分:0)
我会考虑对两个没有相同具体类型的对象返回true的equals实现非常“令人惊讶”的行为。如果你在编译时知道接口的每个可能的实现者的盒子内操作,你可以用仅有接口方法来构造有意义的等号,但这对于API /框架代码来说并不现实。
当你调用用来实现equals的方法时,你甚至无法确定没有人会编写一个改变其内部状态的接口实现!谈论混淆,等于检查返回true并在此过程中使自身无效?
-
就我所知,这是“检查与界面的平等”的问题:
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();}
}
答案 4 :(得分:0)
我尝试在界面中添加另一个equals方法。怎么样:
assertFalse(new Integer(0).equals(new Byte(0))); // pass
assertTrue(new Integer(0).valueEquals(new Byte(0))); // hypothetical pass
这不会产生意外行为(不同的类型相等),但保持开放的可能性以检查相等的值。
在有效的java中有一个相关的主题,其中讨论了与instanceof和getClass的等价。但不记得物品编号。
答案 5 :(得分:0)
可以在不同的类之间定义相等。
在您的情况下,必须由接口指定完全相等的算法,因此实现接口的任何类都必须遵守它。更好的是,由于算法仅依赖于由界面暴露的信息,因此只需实现它,因此子类可以简单地借用它。
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);