Groovy: генерировать методы equals и hashCode - PullRequest
4 голосов
/ 10 сентября 2009

Если у меня есть простой класс 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() потенциально ужасны и их трудно отследить.

Спасибо, Дон

Ответы [ 3 ]

11 голосов
/ 29 ноября 2010

Я не Groovy разработчик, но я понял, что из Groovy 1.8 вы можете вызвать преобразование AST, используя @EqualsAndHashCode для типа.

5 голосов
/ 11 сентября 2009

Или вы можете просто использовать Apache Commons Lang EqualsBuilder и HashCodeBuilder. Вы можете либо позволить строителям использовать Reflection так, чтобы он оценивал все поля, либо определить, какое поле следует включить в вычисления equals() и hashCode().

У них также есть ToStringBuilder, если вам интересно.

2 голосов
/ 24 июня 2010

Если вам нужно чисто 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 (), но это, кажется, нарушает принцип типизации утки для меня. Отрегулируйте по своему вкусу.

Обратите внимание, что если вы находитесь в скрипте, классы в конечном итоге становятся анонимным внутренним классом внутри скрипта, поэтому равенство в любом случае нарушится. Обычные скомпилированные классы не будут страдать от этой же проблемы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...