在我写完标题之后,我读了这个SO post,但仍然决定在Java中讨论有关bug的bug实现问题。这是我的正常实现
@Override
public boolean equals(Object o){
if(o == null) return false;
if(o instanceof CompositePk == false) return false;
if(this == o) return true;
CompositePk that = (CompositePk)o;
return new EqualsBuilder().append(this.id, that.id)
.append(this.bucketId, that.bucketId)
.isEquals();
}
使用Apache的EqualsBuilder来完成平凡的事情。比这更容易的是我的Netbean自动生成equals(o)
实现
@Override
public boolean equals(Object obj){
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TemplatesWrapper other = (TemplatesWrapper) obj;
if (this.timeAdded != other.timeAdded && (this.timeAdded == null || !this.timeAdded.equals(other.timeAdded))) {
return false;
}
return true;
}
我从2个差异项目中获取这些,但他们都试图完成相同的事情,但使用diff方法。您认为哪种风格或是否存在任何缺陷?
答案 0 :(得分:9)
首先,没有必要测试null
,然后测试instanceof,因为foo instanceof Bar
在false
为foo
时评估为null
。
将instanceof
运算符的结果与false
进行比较是很奇怪的,因为instanceof
是一个布尔运算。
将课程与getClass()
进行比较至多是有争议的。 Joshua Bloch撰写了大量Java集合框架以及许多其他重要内容,says
这种技术(“getClass-based equals 方法“)确实满足等于 合同,但成本很高。该 getClass方法的缺点 是它违反了“利斯科夫 替代原则,“陈述 (粗略地说)一种方法 期待一个超类实例必须 当提出一个时,表现得很好 子类实例。如果子类添加 一些新的方法,或平凡的 修改行为(例如,通过发出一个 跟踪每个方法调用), 程序员会很惊讶 子类和超类实例 不要正常交互。对象那个 “应该是平等的”不会,导致 程序失败或表现 不正常。问题是 Java的事实加剧了这一点 集合基于平等 方法
您应该使用instanceof
而不是通过getClass()
进行比较,除非您有某些特定的技术原因。
在确定其他对象与this
相当之后,您可以将基元与==
和对象与equals
进行比较。如果您的任何成员对象可以为null,则会更复杂;然后你必须编写详细的子句来比较可能为空的东西(或写一个bothNullOrEqual(Object a, Object b)
方法)。
EqualsBuilder
方法看起来很虚伪,但那只是一种“气味”,我不会在技术上反对。通常,我不喜欢在可能经常调用的方法中使用额外的方法调用。
Apache的一个是假的,因为它测试null
并使用getClass()
比较。
这是我的:
@Override
public boolean equals(final Object o) {
if (!(o instanceof MyClass))
return false;
final MyClass om = (MyClass)o;
// compare om's fields to mine
}
答案 1 :(得分:5)
我会这样做:
public boolean equals(Object ob) {
if (ob == null) return false;
if (ob == this) return true;
if (!(ob instanceof MyClass)) return false; // OR
if (ob.getClass() != getClass()) return false;
// check relevant members
}
中间的两条线是不同的。一个允许子类相等(第一个),另一个不允许。使用适合的任何一个。
举个例子,Java的AbstractList
类可能会使用第二种形式,因为List
的确切实现是无关紧要的。重要的是,如果成员是平等的,并且处于相同的位置。
相反,Person类应该使用第一个表单(instanceof),因为如果有一个Student子类并且你调用Person.equals(Student)
它可能返回true而不检查Student中的额外字段而Student.equals(Person)将可能会返回false
。如果equals()
不是可交换的,那你就是在寻找麻烦。
我倾向于使用我的IDE(IntelliJ IDEA)生成的equals()
方法,而不是为某些Apache库创建不必要的依赖,但收效甚微。
答案 2 :(得分:0)
Apache比你的更好或者是cletus'。
就我的模糊记忆所暗示,在等号中使用instanceof
存在问题;我还不能完全理解为什么,也许有人会详细说明。我错了。
- 编辑:
由于Chris和Steve有助于下面的解释,我正在考虑平等实施的“symmetric property”。在此基础上,我可以支持我更喜欢Apache实现的主张:)
答案 3 :(得分:0)
老实说,你必须写的代码越少,你就越好(在大多数情况下)。
生成的代码已被许多人调试和使用。您也可以使用生成的内容(如果您需要提高性能,请执行此操作)。
使用生成的代码的优点:只要您的实例字段发生更改(并且此生成的代码未被修改),您就可以简单地重新生成代码。
有时候,考虑可维护性会更容易。经验法则:您自己编写的代码越少,您调试的代码就越少。如果生成的代码没有产生巨大的性能损失,请生成它!
答案 4 :(得分:0)
说明:覆盖equals方法时,hashCode()方法也必须被覆盖。因此,考虑到如下所示的具有3个属性的类,并且考虑到所有属性对于相等都很重要,equals()实现必须测试所有这些字段。条件的顺序并不重要,但必须对所有字段进行相等性测试,以便考虑对象之间的相等性。
public class SampleClass {
private Long id;
private String description;
private Date creation;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((creation == null) ? 0 : creation.hashCode());
result = prime * result + ((description == null) ? 0 : description.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
boolean isEquals = true;
if (this == obj) { isEquals = true; }
else if (obj == null) { isEquals = false; }
else if (getClass() != obj.getClass()) { isEquals = false; }
else {
SampleClass other = (SampleClass) obj;
if (creation == null) {
if (other.creation != null) isEquals = false;
} else if (!creation.equals(other.creation)) {
isEquals = false;
} else if (description == null) {
if (other.description != null) isEquals = false;
} else if (!description.equals(other.description)) {
isEquals = false;
} else if (id == null) {
if (other.id != null) isEquals = false;
} else if (!id.equals(other.id)) {
isEquals = false;
}
}
return isEquals;
}