Java에서 equals 메소드를 재정의하는 방법


108

Java에서 equals 메소드를 재정의하려고합니다. 나는 수업이 People기본적으로이 개 데이터 필드가 nameage. 이제 equals두 사람 객체를 확인할 수 있도록 메서드 를 재정의하고 싶습니다 .

내 코드는 다음과 같습니다.

public boolean equals(People other){
    boolean result;
    if((other == null) || (getClass() != other.getClass())){
        result = false;
    } // end if
    else{
        People otherPeople = (People)other;
        result = name.equals(other.name) &&  age.equals(other.age);
    } // end else

    return result;
} // end equals

그러나 내가 쓸 때 age.equals(other.age)equals 메소드는 String을 비교할 수 있고 age는 Integer이므로 오류가 발생합니다.

해결책

==제안 된대로 연산자를 사용 했고 내 문제가 해결되었습니다.


3
this.age == other.age는 어떻습니까? :)
denis.solonenko

1
연령에 대한 데이터 유형은 무엇입니까? int 또는 정수? 또한 사용중인 JDK 버전은 무엇입니까?
Manish 2011

2
"as equals method can only compare String"-equals method는 String 만 비교할 수 있다고 누가 말 했나요? equals 메소드는 Object 클래스에 속하며 생성 된 모든 클래스는 기본적으로 equals 구현을 갖습니다. 모든 Java 클래스에서 equals를 호출 할 수 있습니다
Manish

답변:


127
//Written by K@stackoverflow
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        ArrayList<Person> people = new ArrayList<Person>();
        people.add(new Person("Subash Adhikari", 28));
        people.add(new Person("K", 28));
        people.add(new Person("StackOverflow", 4));
        people.add(new Person("Subash Adhikari", 28));

        for (int i = 0; i < people.size() - 1; i++) {
            for (int y = i + 1; y <= people.size() - 1; y++) {
                boolean check = people.get(i).equals(people.get(y));

                System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
                System.out.println(check);
            }
        }
    }
}

//written by K@stackoverflow
    public class Person {
        private String name;
        private int age;

        public Person(String name, int age){
            this.name = name;
            this.age = age;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }

            
if (obj.getClass() != this.getClass()) {
                return false;
            }



            final Person other = (Person) obj;
            if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
                return false;
            }

            if (this.age != other.age) {
                return false;
            }

            return true;
        }

        @Override
        public int hashCode() {
            int hash = 3;
            hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
            hash = 53 * hash + this.age;
            return hash;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

산출:

운영:

-Subash Adhikari-VS-K 거짓

-Subash Adhikari-VS-StackOverflow 거짓

-Subash Adhikari-VS-Subash Adhikari 참

-K-VS-StackOverflow 거짓

-K-VS-Subash Adhikari 거짓

-StackOverflow-VS-Subash Adhikari 거짓

-BUILD SUCCESSFUL (총 시간 : 0 초)


7
hash = 53 * hash그것을 사용하는 이유 는 무엇 입니까?
kittu 2015

2
사용 getClass()하면 클래스가 하위 클래스로 분류되고 수퍼 클래스의 객체와 비교되면 문제가 발생할 수 있습니다.
Tuxdude 2015-08-22

1
bcoz 53소수 일 수 있습니다.이 답변을 살펴보십시오. stackoverflow.com/a/27609/3425489 , 그는 숫자를 선택하는 동안 주석을 달았습니다hashCode()
Shantaram Tupe

1
이 질문에 대한 답은 왜 hashCode ()를 재정의하는지에 대한 훌륭한 설명입니다. stackoverflow.com/a/27609/1992108
Pegasaurus

7
instanceof연산자 또는를 사용하는 대신 if (getClass ()! = obj.getClass ()) ... 사용을 고려하십시오 isAssignableFrom. 여기에는 하위 유형 일치가 아닌 정확한 유형 일치가 필요합니다. -대칭 요구 사항. 또한 비교 String하거나 다른 개체 유형을 사용할 수 있습니다 Objects.equals(this.name,other.name).
YoYo

22

매개 변수 유형을 변경하는 새로운 메소드 서명을 도입하는 것을 오버로딩 이라고합니다 .

public boolean equals(People other){

여기는와 People다릅니다 Object.

메서드 시그니처가 수퍼 클래스의 시그니처와 동일하게 유지되는 경우이를 재정의 라고 하며 @Override주석은 컴파일시 두 가지를 구별하는 데 도움이됩니다.

@Override
public boolean equals(Object other){

실제 선언을 보지 않고 age 오류가 나타나는 이유를 말하기가 어렵습니다.


18

전체 코드를 게시하지 않았기 때문에 세부 사항은 잘 모르겠지만 :

  • 재정의하는 것을 잊지 마십시오 hashCode() 하는 것도
  • equals방법이 있어야한다Object 하지, People인수 유형으로. 현재 여러분이 오버로딩하는 것이 아니라 오버로딩하는 것입니다. 특히 나중에 타입을 확인한다면 여러분이 원하는 것이 아닐 것입니다.
  • 당신이 사용할 수있는 instanceof예를 들어 People 객체인지 확인하는 데if (!(other instanceof People)) { result = false;}
  • equals모든 개체에 사용되지만 기본 요소에는 사용되지 않습니다. 나이가 int(원시적) 이라는 뜻이라고 생각합니다.== . Integer (대문자 'I'포함)는 같음과 비교되어야하는 객체입니다.

Java에서 equals 및 hashCode를 재정의 할 때 어떤 문제를 고려해야합니까?를 참조하십시오 . 상세 사항은.


12
@Override
public boolean equals(Object that){
  if(this == that) return true;//if both of them points the same address in memory

  if(!(that instanceof People)) return false; // if "that" is not a People or a childclass

  People thatPeople = (People)that; // than we can cast it to People safely

  return this.name.equals(thatPeople.name) && this.age == thatPeople.age;// if they have the same name and same age, then the 2 objects are equal unless they're pointing to different memory adresses
}

12

항목 10 : 등호를 대체 할 때 일반 계약을 준수하십시오.

Effective Java에 따르면equals 메서드 재정의는 간단 해 보이지만 잘못된 방법은 여러 가지가 있으며 결과는 끔찍할 수 있습니다. 문제를 피하는 가장 쉬운 방법은 equals메서드 를 재정의하지 않는 것 입니다.이 경우 클래스의 각 인스턴스는 자신과 동일합니다. 다음 조건 중 하나가 적용되는 경우 이는 올바른 조치입니다.

  • 클래스의 각 인스턴스는 본질적으로 고유 합니다. 이는 값이 아닌 활성 엔티티를 나타내는 Thread와 같은 클래스에 해당됩니다. Object에서 제공하는 equals 구현은 이러한 클래스에 대해 정확히 올바른 동작을합니다.

  • 클래스가 "논리적 동등성"테스트를 제공 할 필요가 없습니다. 예를 들어 java.util.regex.Pattern은 두 개의 Pattern 인스턴스가 정확히 동일한 정규식을 나타내는 지 확인하기 위해 equals를 재정의 할 수 있었지만 디자이너는 클라이언트가이 기능을 필요로하거나 원할 것이라고 생각하지 않았습니다. 이러한 상황에서는 Object에서 상속 된 equals 구현이 이상적입니다.

  • 수퍼 클래스는 이미 equals를 재정의 했으며 수퍼 클래스 동작은이 클래스에 적합합니다. 예를 들어, 대부분의 Set 구현은 AbstractSet의 equals 구현, AbstractList의 List 구현 및 AbstractMap의 Map 구현을 상속합니다.

  • 클래스는 private 또는 package-private 이며 equals 메서드가 절대로 호출되지 않을 것입니다. 위험을 극도로 회피하는 경우 equals 메서드를 재정 의하여 실수로 호출되지 않도록 할 수 있습니다.

equals메서드는 등가 관계를 구현합니다. 다음과 같은 속성이 있습니다.

  • 재귀 : null이 아닌 참조 값 x에 대해 x.equals(x)true를 반환해야합니다.

  • 대칭 : null 이외의 참조 값의 경우 x와는 y, x.equals(y)그리고 한편 y.equals (X)가 true를 반환하는 경우에만 경우에 true를 반환해야합니다.

  • 트랜은 : null 이외의 참조 값의 경우 x, y, z, 경우 x.equals(y)반환 truey.equals(z)반환 true하고 x.equals(z)반환해야합니다 true.

  • 일관성 : Null이 아닌 참조 값 x및의 y경우 같음 비교에 사용 된 정보가 수정되지 않은 경우를 여러 번 호출하면 x.equals(y)일관 적으로를 반환 true하거나 일관 되게 반환해야합니다 false.

  • null이 아닌 참조 값의 x경우 x.equals(null)를 반환해야합니다 false.

다음은 고품질 equals 방법을위한 레시피입니다.

  1. ==연산자를 사용하여 인수가이 개체에 대한 참조인지 확인합니다. 그렇다면 true를 반환하십시오. 이는 성능 최적화에 불과하지만 잠재적으로 비교 비용이 많이 드는 경우 수행 할 가치가 있습니다.

  2. instanceof연산자를 사용하여 인수의 유형이 올바른지 확인하십시오. 그렇지 않은 경우 false를 반환합니다. 일반적으로 올바른 유형은 메서드가 발생하는 클래스입니다. 가끔이 클래스에 의해 구현 된 일부 인터페이스입니다. 클래스가 인터페이스를 구현하는 클래스 간의 비교를 허용하도록 같음 계약을 구체화하는 인터페이스를 구현하는 경우 인터페이스를 사용합니다. Set, List, Map 및 Map.Entry와 같은 컬렉션 인터페이스에는이 속성이 있습니다.

  3. 인수를 올바른 유형으로 캐스트하십시오. 이 캐스트는 instanceof test가 선행되었으므로 성공할 수 있습니다.

  4. 클래스의 각 "중요한"필드에 대해 인수의 해당 필드가이 개체의 해당 필드와 일치하는지 확인합니다. 이 모든 테스트가 성공하면 true를 반환합니다. 그렇지 않으면 false를 반환합니다. 2 단계의 유형이 인터페이스 인 경우 인터페이스 메서드를 통해 인수의 필드에 액세스해야합니다. 유형이 클래스 인 경우 액세스 가능성에 따라 필드에 직접 액세스 할 수 있습니다.

  5. 유형이 float또는 이 아닌 기본 필드의 경우 비교에 연산자를 double사용하십시오 ==. 개체 참조 필드의 경우 equals메서드를 재귀 적으로 호출합니다 . 위한 float필드 정적 사용 Float.compare(float, float)방법; 과에 대한 double필드 사용 Double.compare(double, double). float 및 double 필드의 특수 처리는 Float.NaN, -0.0f및 유사한 double 값에 의해 필요 합니다. 비교할 수 있지만 floatdouble필드 정적 방법으로 Float.equals하고 Double.equals,이 성능 저하있을 것입니다 모든 비교에 오토 박싱을 수반한다. 들어 array필드, 각 요소에 이러한 지침을 적용 할 수 있습니다. 배열 필드의 모든 요소가 중요한 경우 Arrays.equals방법 중 하나를 사용하십시오 .

  6. 일부 개체 참조 필드에는 합법적으로 null. 가능성을 방지하려면 NullPointerException정적 메서드를 사용하여 해당 필드가 같은지 확인하십시오 Objects.equals(Object, Object).

    // Class with a typical equals method
    
    public final class PhoneNumber {
    
        private final short areaCode, prefix, lineNum;
    
        public PhoneNumber(int areaCode, int prefix, int lineNum) {
    
            this.areaCode = rangeCheck(areaCode,  999, "area code");
    
            this.prefix   = rangeCheck(prefix,    999, "prefix");
    
            this.lineNum  = rangeCheck(lineNum,  9999, "line num");
    
        }
    
        private static short rangeCheck(int val, int max, String arg) {
    
            if (val < 0 || val > max)
    
               throw new IllegalArgumentException(arg + ": " + val);
    
            return (short) val;
    
        }
    
        @Override public boolean equals(Object o) {
            if (o == this)
                return true;
            if (!(o instanceof PhoneNumber))
                return false;
            PhoneNumber pn = (PhoneNumber)o;
            return pn.lineNum == lineNum && pn.prefix == prefix
                    && pn.areaCode == areaCode;
        }
        ... // Remainder omitted
    
    }

1
hashCode()또한 재정의해야한다는 것을 잊지 마십시오 . 또한 Java7 이후로 , 및 , 를 사용 하면 작성 equals()hashCode()메소드가 훨씬 쉬워졌습니다 . Objects.equals()Arrays.equals()Objects.hashCode()Arrays.hashCode()
Arnold Schrijver

3
if (getClass() != obj.getClass()) ...instanceof 연산자를 사용하는 대신 사용을 고려하십시오 . 여기에는 하위 유형 일치가 아닌 정확한 유형 일치 가 필요합니다 . -대칭 요구 사항.
YoYo

@YoYo가 정확합니다 ... instanceof를 사용하면 대칭 속성이 실패 할 수 있습니다. o가 PhoneNumberWithExtension과 같은 PhoneNumber의 하위 클래스이고 instanceof를 사용하여 동일한 방식으로 재정의하는 경우 o.equals (this)는 instanceof 테스트를 실패하고 PhoneNumber.equals는이를 통과하고 true를 반환합니다 (다른 모든 PhoneNumber 필드 가정 같다).
ldkronos

5

내가 추측하기 때문에 age유형은 다음과 int같습니다.

public boolean equals(Object other){
    boolean result;
    if((other == null) || (getClass() != other.getClass())){
        result = false;
    } // end if
    else{
        People otherPeople = (People)other;
        result = name.equals(otherPeople.name) &&  age == otherPeople.age;
    } // end else

    return result;
} // end equals

결과적으로 NullPointerExceptionif nameis null.
orien

@orien 아니 큰 문제가, 어쩌면 그 계약에 name할당되지 없구요 null... 값을
포트란

@fortran 그래서는 ... 어쩌면 큰 문제가 아니다)
orien

5

Java에서 객체를 비교할 때 의미 검사 를 수행하여 객체 의 유형을 비교하고 상태식별 합니다.

  • 자체 (동일한 인스턴스)
  • 자체 (복제 또는 재구성 된 사본)
  • 다른 유형의 다른 개체
  • 같은 유형의 다른 개체
  • null

규칙 :

  • 대칭 :a.equals(b) == b.equals(a)
  • equals()항상 얻을 수 없다 true거나 false, 그러나 결코 NullpointerException, ClassCastException또는 다른 throw 가능 객체

비교:

  • 유형 검사 : 두 인스턴스가 동일한 유형 이어야 합니다. 즉, 실제 클래스가 동일한 지 비교해야합니다. 개발자 instanceof가 유형 비교에 사용할 때 (하위 클래스가없는 한만 작동하며 A extends B -> a instanceof b != b instanceof a).
  • 상태 식별의 의미 검사 : 인스턴스가 식별되는 상태를 이해했는지 확인하십시오. 사람은 사회 보장 번호로 식별 할 수 있지만 머리 색깔 (염색 가능), 이름 (변경 가능) 또는 나이 (항상 변경됨)로는 식별 할 수 없습니다. 값 개체와 만 전체 상태 (모든 비 일시적 필드)를 비교해야하며 그렇지 않으면 인스턴스를 식별하는 항목 만 확인합니다.

귀하의 경우 Person클래스 :

public boolean equals(Object obj) {

    // same instance
    if (obj == this) {
        return true;
    }
    // null
    if (obj == null) {
        return false;
    }
    // type
    if (!getClass().equals(obj.getClass())) {
        return false;
    }
    // cast and compare state
    Person other = (Person) obj;
    return Objects.equals(name, other.name) && Objects.equals(age, other.age);
}

재사용 가능한 일반 유틸리티 클래스 :

public final class Equals {

    private Equals() {
        // private constructor, no instances allowed
    }

    /**
     * Convenience equals implementation, does the object equality, null and type checking, and comparison of the identifying state
     *
     * @param instance       object instance (where the equals() is implemented)
     * @param other          other instance to compare to
     * @param stateAccessors stateAccessors for state to compare, optional
     * @param <T>            instance type
     * @return true when equals, false otherwise
     */
    public static <T> boolean as(T instance, Object other, Function<? super T, Object>... stateAccessors) {
        if (instance == null) {
            return other == null;
        }
        if (instance == other) {
            return true;
        }
        if (other == null) {
            return false;
        }
        if (!instance.getClass().equals(other.getClass())) {
            return false;
        }
        if (stateAccessors == null) {
            return true;
        }
        return Stream.of(stateAccessors).allMatch(s -> Objects.equals(s.apply(instance), s.apply((T) other)));
    }
}

당신을 위해 Person클래스,이 유틸리티 클래스를 사용하여 :

public boolean equals(Object obj) {
    return Equals.as(this, obj, t -> t.name, t -> t.age);
}

1

age가 int이면 ==를 사용해야합니다. Integer 객체이면 equals ()를 사용할 수 있습니다. equals를 재정의하는 경우 해시 코드 메서드도 구현해야합니다. 계약에 대한 자세한 내용은 Object의 javadoc 및 웹의 다양한 페이지에서 확인할 수 있습니다.


0

최근에 사용한 솔루션은 다음과 같습니다.

public class Test {
    public String a;
    public long b;
    public Date c;
    public String d;
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Test)) {
            return false;
        }
        Test testOther = (Test) obj;
        return (a != null ? a.equals(testOther.a) : testOther.a == null)
                && (b == testOther.b)
                && (c != null ? c.equals(testOther.c) : testOther.c == null)
                && (d != null ? d.equals(testOther.d) : testOther.d == null);
    }

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.