(해시 어쩌구 볼 때마다 해시브라운이 생각난다..)
우테코 백엔드 5기 과정 중 학습하며 작성한 내용입니다.
Java의 모든 클래스는 Object 클래스를 상속합니다.
우리가 클래스를 만들 때, extends Obejct
를 써주지 않아도 컴파일러가 이를 알아서 처리해줍니다.
만약 다른 클래스를 상속하더라도 결국 조상으로는 Object 클래스가 있습니다.
즉 Object 클래스는 Java의 모든 클래스에 대한 최고 조상 클래스인 셈이고, 모든 클래스는 Object의 메서드를 사용할 수 있습니다.
Object 클래스에는 총 11개만의 메서드가 있는데, equals와 hashCode는 그 중에서도 재정의(overriding) 할 수 있는 메서드* 중 하나입니다.
그렇다면 내가 만든 클래스에서 이 메서드들을 아무렇게나 원하는 대로 재정의하면 될까요?
관련 규약에 대한 전제를 공유하는 클래스들을 오동작시키지 않으려면, 일반 규약에 맞게 재정의해주어야 합니다.
Object 명세 중 아래 내용이 이 질문(equals 와 hashcode 를 함께 정의해야 하는 이유는?)에 대한 힌트가 될 수 있겠습니다.
equals(Obejct)가 두 객체를 같다고 판단했다면, 두 객체의 hashCode는 똑같은 값을 반환해야 한다.
Hash값을 사용하는 Collection 클래스인 HashMap, HashSet, HashTable은 위 규약을 전제로 하고 있기 때문입니다.
이 클래스들은
1. Hash값이 서로 같고
2. equals의 반환값이 true
일 때 두 객체가 논리적으로 같다고 판단합니다.
예를 들기 위해 equals만 재정의한 클래스 Id를 만들었습니다.
HashMap의 key값으로 이 클래스의 인스턴스를 사용한다면 어떨까요.
public class Id {
private final int value;
public Id(int value) {
this.value = value;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Id id = (Id) o;
return value == id.value;
}
}
아래 테스트를 돌려보면 모두 통과함을 확인할 수 있습니다.
class IdTest {
@Test
void 번호값이_같은_두_아이디는_동등하다() {
Id firstId = new Id(1);
Id secondId = new Id(1);
assertThat(firstId.equals(secondId)).isTrue();
}
@Test
void 해시값이_다르면_다른_객체이다() {
Map<Id, String> names = new HashMap<>();
names.put(new Id(1), "doy");
names.put(new Id(1), "doy");
assertThat(names).hasSize(2);
}
@Test
void 해시값이_다른_객체로_조회할_수_없다() {
Map<Id, String> names = new HashMap<>();
names.put(new Id(1), "doy");
assertThat(names.get(new Id(1))).isNull();
}
}
결론을 정리하자면
Hash값을 사용하는 Collection 클래스를 오동작시키지 않으려면 equals와 hashCode 메서드를 함께 정의해야 합니다.
무엇이든 절대적으로, 항상, 무조건 이라고 말할 수는 없겠지만
Object라는 최고 조상 클래스의 일반 규약에 따라 하위 클래스들이 구현되어있기 때문에
이를 예측 가능한 방식으로 사용하기 위해서는 주어진 규약에 맞게 재정의해야 한다는 생각이 듭니다.
(굳이 재정의를 해야 한다면 말입니다.)
그에 대한 좋은 예시가 equals와 hashCode를 함께 정의해야 하는 이유인 것 같습니다.
참고자료
이펙티브 자바 Effective Java 3/E - 아이템 10. equals는 일반 규약을 지켜 재정의하라
이펙티브 자바 Effective Java 3/E - 아이템 11. equals를 재정의하려거든 hashCode도 재정의하라
테코블 2기_둔덩님 포스트 equals와 hashCode는 왜 같이 재정의해야 할까?
습관적으로 equals와 hashCode를 재정의해서 사용했었는데 ㅎㅎ
테스트코드와 함께 보니 더 좋은 것 같습니다 !!
잘 보고 갑니당