IT TIP

하위 클래스에서 equals () 및 hashCode () 재정의… 수퍼 필드 고려

itqueen 2020. 12. 4. 21:39
반응형

하위 클래스에서 equals () 및 hashCode () 재정의… 수퍼 필드 고려


어떻게 재정의 특정 규칙이 있나요 equals()& hashCode()서브 클래스 고려 슈퍼 필드는 ?? 많은 매개 변수가 있음을 알기 : super 필드는 private / public, getter 유무에 관계없이 ...

예를 들어, Netbeans에서 생성 된 equals () & hashCode ()는 수퍼 필드를 고려하지 않습니다.

    new HomoSapiens("M", "80", "1.80", "Cammeron", "VeryHot").equals(
    new HomoSapiens("F", "50", "1.50", "Cammeron", "VeryHot"))

true를 반환합니다 :(

public class Hominidae {

    public String  gender;
    public String  weight;
    public String  height;

    public Hominidae(String gender, String weight, String height) {
        this.gender = gender;
        this.weight = weight;
        this.height = height;
    }
    ... 
}

public class HomoSapiens extends Hominidae {
    public String name;
    public String faceBookNickname;

    public HomoSapiens(String gender, String weight, String height, 
                       String name, String facebookId) {
        super(gender, weight, height);
        this.name = name;
        this.faceBookNickname = facebookId;
    }
    ...  
}

Netbeans에서 생성 된 equals () & hashCode ()를 보려면 다음을 수행하십시오.

public class Hominidae {

    ...

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Hominidae other = (Hominidae) obj;
        if ((this.gender == null) ? (other.gender != null) : !this.gender.equals(other.gender)) {
            return false;
        }
        if ((this.weight == null) ? (other.weight != null) : !this.weight.equals(other.weight)) {
            return false;
        }
        if ((this.height == null) ? (other.height != null) : !this.height.equals(other.height)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 37 * hash + (this.gender != null ? this.gender.hashCode() : 0);
        hash = 37 * hash + (this.weight != null ? this.weight.hashCode() : 0);
        hash = 37 * hash + (this.height != null ? this.height.hashCode() : 0);
        return hash;
    }

}


public class HomoSapiens extends Hominidae {

    ...

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final HomoSapiens other = (HomoSapiens) obj;
        if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
            return false;
        }
        if ((this.faceBookNickname == null) ? (other.faceBookNickname != null) : !this.faceBookNickname.equals(other.faceBookNickname)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0);
        hash = 89 * hash + (this.faceBookNickname != null ? this.faceBookNickname.hashCode() : 0);
        return hash;
    }
}

자녀는 부모의 사적인 구성원을 조사해서는 안됩니다.

그러나 분명히 모든 중요한 필드는 동등성과 해싱을 위해 고려되어야합니다.

다행히도 두 가지 규칙을 모두 쉽게 충족 할 수 있습니다.

NetBeans에서 생성 한 equals 및 hashcode를 사용하지 않는다고 가정하면 Hominidae의 equals 메소드를 수정하여 클래스 동등성보다는 instanceof 비교를 사용하여 간단하게 사용할 수 있습니다. 이 같은:


    @Override  
    public boolean equals(Object obj) {  
        if (obj == null) { return false; }  
        if (getClass() != obj.getClass()) { return false; }  
        if (! super.equals(obj)) return false;
        else {
           // compare subclass fields
        }

물론 해시 코드는 쉽습니다.


    @Override     
    public int hashCode() {     
        int hash = super.hashCode();
        hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0);     
        hash = 89 * hash + (this.faceBookNickname != null ? this.faceBookNickname.hashCode() : 0);     
        return hash;     
    }     

그래도 진지하게 : 슈퍼 클래스 메서드를 호출하여 슈퍼 클래스 필드를 고려하지 않는 NetBeans는 어떻게됩니까?


저는 commons-lang 패키지EqualsBuilder (및 HashcodeBuilder)를 사용하여 equals () 및 hashcode () 메서드를 훨씬 쉽게 읽을 수 있도록 하는 것을 선호합니다 .

예:

public boolean equals(Object obj) {
 if (obj == null) { return false; }
 if (obj == this) { return true; }
 if (obj.getClass() != getClass()) {
   return false;
 }
 MyClass rhs = (MyClass) obj;
 return new EqualsBuilder()
             .appendSuper(super.equals(obj))
             .append(field1, rhs.field1)
             .append(field2, rhs.field2)
             .append(field3, rhs.field3)
             .isEquals();
}

일반적으로 하위 클래스에서 동등을 구현하는 것은 대칭 및 전이성을 유지하기가 어렵습니다.

해당 필드를 확인하는 슈퍼 클래스를 고려 x하고 y,과에 대한 하위 검사 x, yz.

따라서 Subclass == Superclass == Subclass 여기서 z는 Subclass의 첫 번째 인스턴스와 두 번째 인스턴스 사이에서 다르며 계약의 전이 부분을 위반합니다.

이것이 일반적인 equals 구현이 getClass() != obj.getClass()instanceof 대신 확인하는 이유 입니다. 위의 예에서 SubClass 또는 Superclass가 instanceof check를 수행하면 대칭이 깨집니다.

따라서 결과는 서브 클래스가 확실히 super.equals ()를 고려할 수 있지만 위의 문제를 방지하기 위해 자체 getClass () 확인을 수행 한 다음 자체 필드에서 동등 함을 추가로 확인해야한다는 것입니다. 수퍼 클래스가 같음을 반환하는 경우가 아니라 수퍼 클래스의 특정 필드를 기반으로 자신의 같음 동작을 변경 한 클래스의 이상한 오리 일 것입니다.


규칙은 다음과 같습니다.

  • 이는 반사적입니다. null이 아닌 참조 값 x에 대해 x.equals (x)는 true를 반환해야합니다.
  • 이것은 대칭 적입니다. null이 아닌 참조 값 x 및 y에 대해 x.equals (y)는 y.equals (x)가 true를 반환하는 경우에만 true를 반환해야합니다.
  • 전 이적입니다. null이 아닌 참조 값 x, y 및 z에 대해 x.equals (y)가 true를 반환하고 y.equals (z)가 true를 반환하면 x.equals (z)가 true를 반환해야합니다.
  • 일관성이 있습니다. null이 아닌 참조 값 x 및 y에 대해 x.equals (y)를 여러 번 호출하면 객체에 대한 같음 비교에 사용 된 정보가 수정되지 않은 경우 일관되게 true를 반환하거나 false를 반환합니다.
  • null이 아닌 참조 값 x의 경우 x.equals (null)은 false를 반환해야합니다.
  • 일반적으로이 메서드가 재정의 될 때마다 hashCode 메서드를 재정의하는 데 필요합니다. 따라서 동일한 객체는 동일한 해시 코드를 가져야한다는 hashCode 메서드에 대한 일반 계약을 유지합니다.

Object.equals () .

따라서 규칙을 이행하는 데 필요한 필드를 사용하십시오.


상속은 캡슐화를 깨기 때문에 equals () 및 hashCode ()를 구현하는 서브 클래스는 반드시 수퍼 클래스의 특성을 고려해야합니다. 하위 클래스의 메서드에서 부모 클래스의 equals () 및 hashCode () 메서드에 대한 호출을 성공적으로 인코딩했습니다.


받아 들여진 @CPerkins 대답과 관련하여, 나는 super.equals () 메서드가 클래스 동등성을 검사 할 가능성이 있기 때문에 주어진 equals () 코드가 안정적으로 작동 할 것이라고 생각하지 않습니다. 서브 클래스와 슈퍼 클래스는 동일한 클래스를 갖지 않습니다.


음, HomoSapiens#hashcodeCPerkins의 답변으로 충분할 것입니다.

@Override     
public int hashCode() {     
    int hash = super.hashCode();
    hash = 89 * hash + Objects.hash(name);     
    hash = 89 * hash + Objects.hash(faceBookNickname);     
    return hash;     
}

당신이 그 부모의 필드 (원하는 경우 gender, weight, height행동)을 한 가지 방법은 부모 유형의 실제 인스턴스를 생성하고 사용한다. 감사합니다. 추상 클래스가 아닙니다.

@Override
public boolean equals(Object obj) {
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final HomoSapiens other = (HomoSapiens) obj;
    if (!super.equals(new Hominidae(
        other.gender, other.weight, other.height))) {
         return false;
    }
    if (!Objects.equals(name, other.name)) return false;
    if (!Objects.equals(faceBookNickname, other.faceBookNickname))
        return false;
    return true;
}

나는 이것을 해결하는 방법을 추가하고 있습니다. 요점은 평등을 느슨하게 확인하는 메서드를 추가하는 것입니다.

public class Parent {

    public Parent(final String name) {
        super(); this.name = name;
    }

    @Override
    public int hashCode() {
        return hash = 53 * 7 + Objects.hashCode(name);
    }

    @Override
    public boolean equals(final Object obj) {
        return equalsAs(obj) && getClass() == obj.getClass();
    }

    protected boolean equalsAs(final Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (!getClass().isAssignableFrom(obj.getClass())) return false;
        final Parent other = (Parent) obj;
        if (!Objects.equals(name, other.name)) return false;
        return true;
    }

    private final String name;
}

그리고 여기에 Child.

public class Child extends Parent {

    public Child(final String name, final int age) {
        super(name); this.age = age;
    }

    @Override
    public int hashCode() {
        return hash = 31 * super.hashCode() + age;
    }

    @Override
    public boolean equals(final Object obj) {
        return super.equals(obj);
    }

    @Override
    protected boolean equalsAs(final Object obj) {
        if (!super.equalsAs(obj)) return false;
        if (!getClass().isAssignableFrom(obj.getClass())) return false;
        final Child other = (Child) obj;
        if (age != other.age) return false;
        return true;
    }

    private final int age;
}

테스트 중 ...

@Test(invocationCount = 128)
public void assertReflective() {
    final String name = current().nextBoolean() ? "null" : null;
    final int age = current().nextInt();
    final Child x = new Child(name, age);
    assertTrue(x.equals(x));
    assertEquals(x.hashCode(), x.hashCode());
}

@Test(invocationCount = 128)
public void assertSymmetric() {
    final String name = current().nextBoolean() ? "null" : null;
    final int age = current().nextInt();
    final Child x = new Child(name, age);
    final Child y = new Child(name, age);
    assertTrue(x.equals(y));
    assertEquals(x.hashCode(), y.hashCode());
    assertTrue(y.equals(x));
    assertEquals(y.hashCode(), x.hashCode());
}

@Test(invocationCount = 128)
public void assertTransitive() {
    final String name = current().nextBoolean() ? "null" : null;
    final int age = current().nextInt();
    final Child x = new Child(name, age);
    final Child y = new Child(name, age);
    final Child z = new Child(name, age);
    assertTrue(x.equals(y));
    assertEquals(x.hashCode(), y.hashCode());
    assertTrue(y.equals(z));
    assertEquals(y.hashCode(), z.hashCode());
    assertTrue(x.equals(z));
    assertEquals(x.hashCode(), z.hashCode());
}

부모 (슈퍼) 클래스가 같음을 재정의하지 않는 것 같습니다. 이 경우 하위 클래스에서이 메서드를 재정의 할 때 상위 클래스의 필드를 비교해야합니다. 나는 커먼즈 EqualsBuiler를 사용하는 것이 갈 길이라는 데 동의하지만 동등 계약의 대칭 / 전이 부분을 깨지 않도록주의해야합니다.

If your sub-class adds attributes to the parent class and the parent class isn't abstract and overrides equals you're going to get in to trouble. In this scenario you should really look at object composition instead of inheritance.

I'd strongly recommend the section in Effective Java by Joshua Block on this. It's comprehensive and really well explained.


It's worth noting that the IDE auto generation maybe has taken into consideration super class,just provided that the equals() and hashCode() of super class exists yet. That is, should auto generate these two functions of super first, and then auto generate of the child. I got below right example under Intellj Idea:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    if (!super.equals(o)) return false;

    TActivityWrapper that = (TActivityWrapper) o;

    return data != null ? data.equals(that.data) : that.data == null;
}

@Override
public int hashCode() {
    int result = super.hashCode();
    result = 31 * result + (data != null ? data.hashCode() : 0);
    return result;
}

The problem happens just when you don't auto generate super's in first. Please check above under Netbeans.


I believe they now have a method that just does this for you :

EqualsBuilder.reflectionEquals(this, o);

HashCodeBuilder.reflectionHashCode(this);

참고URL : https://stackoverflow.com/questions/2066917/overriding-equals-hashcode-in-sub-classes-considering-super-fields

반응형