java equals () 메소드 재정의-작동하지 않습니까?


150

나는 equals()오늘 시험 법 에 대한 흥미롭고 매우 실망스러운 문제 에 부딪쳤다.

완벽 함을 위해 IDE 또는 디버거를 사용하지 않았습니다. 좋은 구식 텍스트 편집기와 System.out입니다. 시간은 매우 제한되어 있었고 학교 프로젝트였습니다.

어쨌든-

나는 포함 할 수있는 기본적인 쇼핑 카트를 개발하고 ArrayListBook객체 . 구현하기 위해 addBook(), removeBook()그리고 hasBook()카트의 방법을, 나는 여부를 확인하고 싶었다 Book이미 존재 Cart. 그래서 나는 간다-

public boolean equals(Book b) {
    ... // More code here - null checks
    if (b.getID() == this.getID()) return true;
    else return false;
}

테스트에서 모두 잘 작동합니다. 6 개의 객체를 만들어 데이터로 채 웁니다. 에 대해 많은 추가, 제거, has () 작업을 수행하면 Cart모든 것이 잘 작동합니다. 나는 당신 equals(TYPE var)또는equals(Object o) { (CAST) var } 하지만이 작동 된 이후, 너무 많이 중요하지 않았다 것으로 가정.

그럼 난 문제로 실행 - 내가 만드는 데 필요한 Book와 개체를 ID Book 클래스 내에서한다. 다른 데이터는 입력되지 않습니다. 기본적으로 다음과 같습니다.

public boolean hasBook(int i) {
    Book b = new Book(i);
    return hasBook(b);
}

public boolean hasBook(Book b) {
    // .. more code here
    return this.books.contains(b);
}

갑자기 equals(Book b)방법이 더 이상 작동하지 않습니다. 좋은 디버거없이 추적하는 데 시간이 오래 걸렸으며 Cart클래스가 올바르게 테스트되고 수정 되었다고 가정합니다 . equals()다음과 같은 방법으로 스와핑 한 후 :

public boolean equals(Object o) {
    Book b = (Book) o;
    ... // The rest goes here   
}

모든 것이 다시 작동하기 시작했습니다. 방법이 명확에도 예약 매개 변수를 취할하지 않기로 결정 이유가 거기에 있었다Book 목적은? 유일한 차이점은 동일한 클래스 내에서 인스턴스화되었으며 하나의 데이터 멤버로 채워진 것 같습니다. 매우 혼란 스러워요. 제발 빛 좀 비춰 줘?


1
나는 반사를 통해 equals 메소드를 재정의하는 것과 관련하여 '계약'을 위반했다는 것을 알고 있습니다. 그러나 객체를 제네릭을 사용하지 않고 ArrayList에 존재했는지 확인하는 빠른 방법이 필요했습니다.
Josh Smeaton

1
이것은 자바와 동등에 대해 배우는 좋은 교훈입니다
jjnguy

답변:


329

Java에서 equals()상속 된 메소드 Object는 다음과 같습니다.

public boolean equals(Object other);

즉, 매개 변수는 유형이어야합니다 Object. 이를 재정의 라고합니다 . 귀하의 방법 public boolean equals(Book other)오버로드 라는 것을 수행합니다.equals() .

ArrayList오버라이드 (override) 사용 equals()방법 (자사에 대한 예를 내용을 비교 contains()하고 equals(), 방법) 하지 과부하 사람. 대부분의 코드에서 Objectequals를 올바르게 재정의하지 않은 코드를 호출하는 것은 좋지만와 호환되지 않습니다 ArrayList.

따라서 메소드를 올바르게 재정의하지 않으면 문제가 발생할 수 있습니다.

매번 다음을 무시합니다.

@Override
public boolean equals(Object other){
    if (other == null) return false;
    if (other == this) return true;
    if (!(other instanceof MyClass)) return false;
    MyClass otherMyClass = (MyClass)other;
    ...test other properties here...
}

@Override주석을 사용하면 바보 같은 실수로 많은 도움이 될 수 있습니다.

수퍼 클래스 또는 인터페이스의 메서드를 재정의한다고 생각 될 때마다 사용하십시오. 그렇게하면 잘못된 방법으로 컴파일 오류가 발생합니다.


31
이것은 OP가 @Override 어노테이션을 선호하는 좋은 주장이다. 만약 OP가 @Override를 사용했다면 그의 컴파일러는 실제로 부모 클래스 메소드를 오버라이드하지 않았다고 말할 것이다.
Cowan

1
@Override를 알지 못했습니다. 감사합니다! 또한 재정의 hashCode ()가 실제로 수행되었으며 오류를 더 일찍 발견했을 수도 있다고 덧붙이고 싶습니다.
Josh Smeaton

5
일부 IDE (예 : Eclipse)는 클래스 멤버 변수를 기반으로 equals () 및 hashcode () 메소드를 자동 생성 할 수도 있습니다.
sk.

1
if (!(other instanceof MyClass))return false;다른 클래스를 확장하면를 반환 false합니다 MyClass. 그러나 false다른 클래스가 확장되면 반환되지 않습니다 MyClass. equal덜 모순 되지 않아야합니까?
Robert

19
이전 nullcheck의 인스턴스를 사용할 때 중복됩니다.
Mateusz Dymczyk

108

일식을 사용하는 경우 상단 메뉴로 이동하십시오.

소스-> equals () 및 hashCode () 생성


나는 동의한다! 내가 전에 알지 못했던 것을 생성하면 오류가 덜 발생합니다
Boy

여기도 마찬가지입니다. 고마워 프레드!
Anila

16
IntelliJ의 코드 → 생성… 또는 control + N에 있습니다. :)
오른쪽

Netbeans에서 메뉴 표시 줄> 소스 (또는 마우스 오른쪽 버튼 클릭)> 코드 삽입 (또는 Ctrl-I)으로 이동하여 equals () 생성 ...을 클릭하십시오.
Solomon

11

귀하의 질문에 약간의 주제가 없지만 어쨌든 언급 할 가치가 있습니다.

Commons Lang 은 equals와 hashcode를 재정의하는 데 사용할 수있는 훌륭한 방법을 가지고 있습니다. 체크 아웃 EqualsBuilder.reflectionEquals (...)HashCodeBuilder.reflectionHashCode (...) . 과거에 많은 두통을 피했습니다. 물론 ID에서 "동일"을 원한다면 상황에 맞지 않을 수 있습니다.

또한 @Overrideequals (또는 다른 방법)를 재정의 할 때마다 주석 을 사용해야한다는 것에 동의합니다 .


4
일식 사용자 인 경우 다음을 수행 할 수도 있습니다 right click -> source -> generate hashCode() and equals().
tunaranch

1
이 메소드가 런타임에 실행되는 것이 맞습니까? 리플렉션으로 인해 다른 항목과의 동등성을 검사하는 항목으로 큰 컬렉션을 탐색하는 경우 성능 문제가 발생하지 않습니까?
Gaket

4

상용구 코드를 저장하는 또 다른 빠른 솔루션은 Lombok EqualsAndHashCode 주석 입니다. 쉽고 우아하며 사용자 정의 할 수 있습니다. 그리고 IDE에 의존하지 않습니다 . 예를 들어;

import lombok.EqualsAndHashCode;

@EqualsAndHashCode(of={"errorNumber","messageCode"}) // Will only use this fields to generate equals.
public class ErrorMessage{

    private long        errorNumber;
    private int         numberOfParameters;
    private Level       loggingLevel;
    private String      messageCode;

등호에 사용할 필드를 사용자 정의하려면 사용 가능한 옵션을 참조하십시오 . 롬복은 maven에서 사용할 수 있습니다. 제공된 범위로 추가하십시오 .

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.14.8</version>
    <scope>provided</scope>
</dependency>

1

Android Studio에서는 alt + insert ---> equals 및 hashCode입니다.

예:

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

    Proveedor proveedor = (Proveedor) o;

    return getId() == proveedor.getId();

}

@Override
public int hashCode() {
    return getId();
}

1

치다:

Object obj = new Book();
obj.equals("hi");
// Oh noes! What happens now? Can't call it with a String that isn't a Book...

1
@Elazar 어떻게? obj로 선언됩니다 Object. 상속의 요점은 다음을 할당 할 수 있습니다 Bookobj. 그 후,을 via Object와 비교할 수 없다고 제안하지 않는 한 ,이 코드는 완벽하게 합법적이어야합니다 . Stringequals()false
bcsb1001

나는 그것을 정확하게 제안합니다. 나는 그것이 꽤 널리 받아 들여 졌다고 믿는다.
Elazar

0

instanceOf문은 종종 등호의 구현에 사용됩니다.

이것은 인기있는 함정입니다!

문제는 사용 instanceOf이 대칭 규칙을 위반한다는 것입니다.

(object1.equals(object2) == true) 만약에 (object2.equals(object1))

첫 번째 equals가 true이고 object2가 obj1이 속한 클래스의 하위 클래스 인스턴스 인 경우 두 번째 equals는 false를 반환합니다!

ob1이 속하는 것으로 간주되는 클래스가 final로 선언되면이 문제는 발생할 수 없지만 일반적으로 다음과 같이 테스트해야합니다.

this.getClass() != otherObject.getClass(); 그렇지 않으면 false를 반환하고 그렇지 않으면 필드를 테스트하여 동등성을 비교하십시오!


3
메소드 재정의 문제를 논의하는 큰 섹션 인 Bloch, Effective Java, 항목 8을 참조하십시오 equals(). 그는 사용하지 않는 것이 좋습니다getClass() . 주된 이유는 평등에 영향을 미치지 않는 하위 클래스에 대한 Liskov 대체 원칙을 위반하기 때문입니다.
스튜어트 마크

-1

recordId는 객체의 속성입니다

@Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Nai_record other = (Nai_record) obj;
        if (recordId == null) {
            if (other.recordId != null)
                return false;
        } else if (!recordId.equals(other.recordId))
            return false;
        return true;
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.