2009-09-10 3 views
4

나는 간단한 그루비 클래스가있는 경우 등 내가 쓰기 (또는 생성하는 IDE를 사용) hashCodeequals 방법처럼 수 있지만그루비 : 등호와 hashCode 메소드를 생성

class Address { 

    Integer streetNumber 
    String streetName 
    String state 
    String zip 
    Country country  
} 

등 :

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()을 엉망으로 만들 때의 결과는 잠재적으로 무의미하고 추적하기가 힘들 기 때문에 내 자신의 솔루션을 조금 조심해야합니다.

감사합니다, 돈

답변

11

나는 그루비 개발자가 아니지만 groovy 1.8에서 유형에 @EqualsAndHashCode를 사용하여 AST 변환을 호출 할 수 있음을 이해했습니다.

+0

참고로,이 기능은 Groovy 1.8-beta-1에서만 사용할 수 있습니다. Groovy 1.8은 2010 년 말에 출시 될 예정입니다. 자세한 내용은 http://groovy.codehaus.org/News –

+2

을 참조하십시오. 공개/보호/개인 액세스 수정자가있는 필드가 아닌 _properties_ 만 고려합니다. 어노테이션에 * includeFields = true *를 지정하십시오. –

5

아니면 그냥 Apache Commons LangEqualsBuilderHashCodeBuilder를 사용할 수 있습니다. 빌더가 Reflection을 사용하여 모든 필드를 평가하거나 equals()hashCode() 계산에 포함되어야하는 필드를 식별하도록 할 수 있습니다.

관심이 있으시면 ToStringBuilder도 있습니다. 당신이 순수 그루비 솔루션을 원하는 경우

+0

감사합니다.이 기능은 Groovy에서 잘 작동합니다! –

+0

주의 사항 - 반사 방식에는 상당한 성능 비용이 있으므로 일반적으로 반사 방식을 사용하지 않는 것이 좋습니다. –

2

, 당신이 뭔가를 할 수 있습니다 :

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() 메소드처럼 클래스가 일치하는지 확인해야하지만 이는 오리 타이핑 원리를 위반하는 것으로 보입니다. 취향에 맞게 조정하십시오.

스크립트에있는 경우 클래스는 스크립트 내에서 익명의 내부 클래스이므로 결국 동일하게 실패합니다. 보통 컴파일 된 클래스는이 같은 문제를 겪지 않습니다.

+0

groovy.lang.MissingPropertyException가 발생합니다. obj에 이름 또는 숫자 속성이없는 경우 해당 속성이 없습니다. – rochb

관련 문제