Java Groovy:生成equals和hashCode方法

Java Groovy:生成equals和hashCode方法,java,groovy,Java,Groovy,如果我有一个简单的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()

如果我有一个简单的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()
的后果可能很可怕,很难追踪

谢谢, 不要

或者您可以使用的和。您可以让构建器使用反射,这样它将计算所有字段,或者确定在
equals()
hashCode()
计算中应该包括哪个字段


如果您感兴趣,他们还有一个解决方案。

如果您想要纯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()方法中一样,但这似乎违反了duck类型原则。根据你的口味进行调整


请注意,如果您在脚本中,那么这些类最终会成为脚本中的匿名内部类,因此平等性无论如何都会失败。普通编译类不会遇到同样的问题。

我不是groovy开发人员,但我知道在groovy 1.8中,您可以在类型上使用@EqualsAndHashCode调用AST转换。

仅供参考,此功能仅在groovy 1.8-beta-1中可用。Groovy 1.8计划于2010年底发布。有关详细信息,请参见此处,并注意它只考虑属性(不考虑具有公共/受保护/私有访问修饰符的字段)除非在注释中指定includefelds=true,否则我将获得groovy.lang.MissingPropertyException:如果obj没有name或number属性,则不会获得此类属性。请注意,反射方法会带来很大的性能代价,因此通常最好避免这些。