본문 바로가기
책/이펙티브 자바

자바 equals와 hashCode

by jeounpar 2023. 7. 4.

일반적으론 equals는 재정의할 일이 없지만 다음과 같은 상황에선 재정의를 해줘야 한다.

객체 식별성(Object Identity; 두 객체가 물리적으로 같은가)이 아닌 논리적으로 같은지 확인해야 할 때이다.

 

핸드폰 번호를 저장하는 예시를 보자.

다음과 같은 PhoneNumber 클래스가 있고

public class PhoneNumber {
	private String a;
	private String b;
	private String c;

	private PhoneNumber() {
	}

	public static PhoneNumber createPhoneNumber(String a, String b, String c) {
		PhoneNumber phoneNumber = new PhoneNumber();
		phoneNumber.a = a;
		phoneNumber.b = b;
		phoneNumber.c = c;
		return phoneNumber;
	}
}

PhoneNumber 객체를 Map의 key로 하고 value로는 사람의 이름을 넣어보자.

Map<PhoneNumber, String> hashMap = new HashMap<>();
hashMap.put(PhoneNumber.createPhoneNumber("010", "1234", "1234"), "Park");
String value = hashMap.get(PhoneNumber.createPhoneNumber("010", "1234", "1234"));
System.out.println("value = " + value);

예상 되는 값은 아마도 "Park"이지만 value변수에는 null값이 담긴다.

 

우린 PhoneNumber객체의 물리적 값이 아닌 논리적 값이 일치하는 것에 의도를 두고 Map에 key값으로 넣었지만 key값을 찾지 못했다.

이런 경우에 equals메서드를 재정의 해줘야 한다.

equals 메서드를 재정의할 때는 반드시 아래의 일반 규약을 지켜야 한다.

Effective Java

하지만 equals메서드를 재정의했다고 해서 value변수에 "Park"값이 담기진 않는다.

 

Object 명세에는 다음과 같은 규약이 있다.

* equals메서드는 두 객체를 같다고 판단했다면, 두 객체의 hashCode는 똑같은 값을 반환해야 한다.

즉, hashMap에 담긴 PhoneNumber객체와 새로 생성한 PhoneNumber객체는 같다고 봐야 하는데 hashCode를 재정의하지 않았으므로 물리적인 값을 비교하여 value에 null값이 담기는 것이다.

 

다음과 같이 equals와 hashCode를 재정의 해주면

@Override
public boolean equals(Object o) {
	if (this == o) return true;
	if (o == null || getClass() != o.getClass()) return false;
	PhoneNumber that = (PhoneNumber) o;
	if (!a.equals(that.a)) return false;
	if (!b.equals(that.b)) return false;
	return c.equals(that.c);
}

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

의도한 대로 value에 "Park"을 HashMap에서 꺼내오는 것을 볼 수 있다.

 

equals메서드는 일반적인 상황에서는 재정의할 필요가 없으며, 재정의가 필요할 때는 hashCode도 재정의해서 의도한 대로 프로그램이 동작하게 하자.