Effective Java - Item 10

Updated:

Effective Java

Item 10. equals는 일반 규약을 지켜 재정의하라

equals 메서드는 재정의가 하기 쉬워 보이지만 잘못된 재정의로 문제가 발생하는 경우가 많다.
이러한 문제를 회피하는 가장 쉬운 길은 아예 재정의를 하지 않는 것이다.

equals 재정의를 하지 않아도 되는 경우

  1. 값을 표현하는 것이 아니라 동작하는 개체를 표현하는 클래스
    Ex) Thread
  2. 인스턴스의 논리적 동치성을 검사할 일이 없다.
  3. 상위 클래스에서 재정의한 equals가 하위 클래스에도 맞는다.
  4. 클래스가 private / package-private이며 equals를 호출할 일이 없다.

equals 재정의를 해야할 경우

객체 식별성(두 객체가 물리적으로 같은가?(주소가 같은가?))가 아닌 논리적 동치성을 확인해야 하는 경우.
주로 값 클래스들이 해당하며 Integer와 String과 같은 값을 나타내는 클래스의 경우다.
두 값 객체의 주소가 아닌 가지고 있는 을 비교하는 것이다.

값 클래스라 해도, 값이 같은 인스턴스가 둘 이상 만들어지지 않음을 보장하면(Item 1) equals를 재정의 하지 않아도 된다.

equals 메서드를 재정의 하기 위한 규약

  • 반사성 : x.equals(x)는 항상 true
  • 대칭성 : x.equals(y)가 true면 y.equals(x)도 항상 true
  • 추이성 : x.equals(y)가 true, y.equals(z)도 true이면, x.equals(z)도 항상 true
  • 일관성 : x.equals(y)를 반복하여 호출하면 항상 true 이거나 항상 false를 반환한다.
  • null-아님 : x.euqals(null)은 항상 false 이다.

equals 메서드 구현 방법

  1. == 연산자를 사용해 입력이 자기 자신의 참조인지 확인한다.
    단순히 자기 자신과 비교하면 항상 true이다.
  2. instanceof 연산자로 입력이 올바른 타입인지 확인한다.
    자신과 다른 instance를 가진 객체면 false를 반환한다.
  3. 입력을 올바른 타입으로 형변환한다.
  4. 비교하는 객체의 모든 핵심 필드들이 모두 일치하는지 하나씩 확인한다.

다음은, 위 구현 4가지 구현방법을 통해 구현한 전형적인 equals 메서드의 예이다.


public class EqualsTest {
	private int a,b,c;

	public EqualsTest(int a, int b, int c) {
		super();
		this.a = a;
		this.b = b;
		this.c = c;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + a;
		result = prime * result + b;
		result = prime * result + c;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {	// 1번
			return true;
		}
		if (!(obj instanceof EqualsTest)) {	// 2번
			return false;
		}
		EqualsTest other = (EqualsTest) obj; // 3번
		return a == other.a && b == other.b && c == other.c;	// 4번
	}	
}

마지막 주의사항

  • 최상의 성능을 바란다면 다를 가능성이 더 크거나 비교하는 비용이 싼 필드를 먼저 비교하자
  • equals를 재정의할 땐 hashCode도 반드시 재정의하자(item 11)
  • 필드들의 동치성만 검사하자. 너무 복잡하게 해결할 필요 없다
  • Object 외의 타입을 매개변수로 받는 equals 메서드는 선언하지 말자
    public boolean equals(MyClass o){ // 반드시 Object o로 매개변수를 받자
    // 잘못된 예
    }
    
  • 되도록이면 AutoValue 프레임워크를 사용하자

정리

꼭 필요한 경우가 아니면 equals를 재정의하지 말자. 많은 경우에 Object의 equals가 여러분이 원하는 비교를 정확히 수행해준다.
재정의해야 할 때는 그 클래스의 핵심 필드 모두를 빠짐없이, 다섯 가지 규약을 확실히 지켜가며 비교해야 한다.

출처

Joshua Bloch. Effective Java 3/E. n.p.: 인사이트, 2018년 11월 1일.

참고 페이지