如果我有一个简单的Groovy类,比如
class Address {
Integer streetNumber
String streetName
String state
String zip
Country country
}
虽然我可以编写(或使用IDE生成)hashCode
和equals
方法,例如:
boolean equals(o) {
if (this.is(o)) return true;
if (!o || getClass() != o.class) return false;
Address that = (Address) o;
if (streetNumber? !streetNumber.equals(that.streetNumber) : that.streetNumber!= null) return false;
if (streetName? !streetName.equals(that.streetName) : that.streetName!= null) return false;
if (state? !state.equals(that.state) : that.state!= null) return false;
if (zip? !zip.equals(that.zip) : that.zip!= null) return false;
if (country? !zip.equals(that.zip) : that.zip!= null) return false;
return true;
}
int hashCode() {
int result = (streetNumber ? streetNumber.hashCode() : 0);
result = 31 * result + (streetName ? streetName.hashCode() : 0);
result = 31 * result + (state ? state.hashCode() : 0);
result = 31 * result + (zip ? zip.hashCode() : 0);
return 31 * result + (country ? country.hashCode() : 0);
}
虽然这样可以正常工作,但我觉得我可以更好地利用Groovy的动力来在更少的代码中实现相同的功能。我想到的一种方法是使用.properties
来获取对象的属性名称和值的映射。然后我可以迭代这些属性,在每个属性上调用hashCode()
或equals()
以获得与上面相同的结果。
在我走这条路之前,我只想检查是否有其他人找到了解决这个问题的好方法。我对推出自己的解决方案有点警惕,因为弄乱equals()
或hashCode()
的后果可能很难以追查并且很难追踪。
谢谢, 唐
答案 0 :(得分:11)
我不是一个常规的开发人员,但我理解,从groovy 1.8开始,您可以使用类型上的@EqualsAndHashCode调用AST转换。
答案 1 :(得分:5)
或者您可以使用Apache Commons Lang的EqualsBuilder
和HashCodeBuilder
。您可以让构建器使用Reflection,以便它将评估所有字段或确定equals()
和hashCode()
计算中应包含哪个字段。
如果您有兴趣,他们也有ToStringBuilder
。
答案 2 :(得分:2)
如果你想要一个纯粹的Groovy解决方案,你可以这样做:
interface DefaultEquality {}
DefaultEquality.metaClass.hashCode = {
delegate.properties.inject(1) { hash, property ->
if (property.key == "class" || property.key == "metaClass") {
hash
} else {
31 * hash + (property.value?.hashCode() ?: 0)
}
}
}
DefaultEquality.metaClass.equals = { obj ->
def outerDelegate = delegate
outerDelegate.properties.inject(true) { equals, property ->
if (property.key == "metaClass") {
equals
} else {
equals && outerDelegate[property.key] == obj[property.key]
}
}
}
class Foo implements DefaultEquality {
String name
Integer number
}
def a1 = new Foo()
def b1 = new Foo(name: "Delphyne")
def c1 = new Foo(number: 1)
def d1 = new Foo(name: "Delphyne", number: 1)
def a2 = new Foo()
def b2 = new Foo(name: "Delphyne")
def c2 = new Foo(number: 1)
def d2 = new Foo(name: "Delphyne", number: 1)
assert a1 == a2 && a1.hashCode() == a2.hashCode()
assert b1 == b2 && b1.hashCode() == b2.hashCode()
assert c1 == c2 && c1.hashCode() == c2.hashCode()
assert d1 == d2 && d1.hashCode() == d2.hashCode()
您还可以实现执行相同操作的AST转换。您可能还应该检查类是否匹配,就像在传统的equals()方法中一样,但这似乎违反了对我来说鸭子类型的原则。根据自己的喜好调整。
请注意,如果您在脚本中,那么这些类最终会成为脚本中的匿名内部类,因此无论如何都会失败。正常编译的类不会遇到同样的问题。