equals를 재정의한 클래스 모두에서 hashCode도 재정의해야 한다.
만약 위의 원칙을 어긴다면 hashCode의 일반 규약을 어기게 된다. (HashMap, HashSet에 해당 인스턴스를 사용 시 문제 발생)
https://jwdeveloper.tistory.com/159
equals(Obj) 가 두 객체를 같다고 판단하였으면, 두 객체의 hashCode는 똑같은 값을 반환해야 한다.
객체를 Key 로 두고 Value를 junwoo 라고 하였다.
get 을 이용해서 해당 Key를 호출하였지만 결과는 junwoo가 아닌 null이이다.
/**
* Project : EffectiveStudy
* Created by InteliJ IDE
* Developer : junwoochoi
* Date : 09/02/2020
* Time : 10:32 오후
*/
public class equalsWithHashcode {
public static void main(String[] args) {
Map<PhoneNumber, String> m = new HashMap<>();
m.put(new PhoneNumber(707,867,5309),"junwoo");
String result = m.get(new PhoneNumber(707, 867, 5309));
System.out.println(result);
}
}
이유는 객체를 두번 생성하였기 때문에 각각의 객체의 주소가 다르기 때문이다.
또 다른 이유는 hashCode를 재정의하지 않았기 때문에 Object 클래스의 정의된 hashCode의 디폴트 값을 참조하기 때문이다.
만약 객체 생성시 hashCode를 확인하여 출력한다면 알맞은 결과를 확인할 수 있다.
public class equalsWithHashcode {
public static void main(String[] args) {
Map<Integer, String> m = new HashMap<>();
m.put(new PhoneNumber(707,867,5309).hashCode(),"junwoo");
Iterator iterator = m.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
System.out.println("key :: "+ entry.getKey());
}
String result2 = m.get(1927950199);
System.out.println(result2);
}
}
두개의 인스턴스를 같은 버킷에 담았다고 하더라도 해시코드는 다르기에 여전히 null을 반환한다.
똑같은 해시코드를 리턴한다면 결과는 어떻게 될까?
42를 리턴하는 hashCode를 재정의하자
@RunWith(JUnit4.class)
public class equalsWithHashcodeTest {
@Test
public void hashCodeOverride() {
HashMap<ExtendedPhoneNumber, String> map = new HashMap<>();
map.put(new ExtendedPhoneNumber(707,867,5307), "제니");
System.out.println("HashCode of Instance 1 :: "+ new ExtendedPhoneNumber(707,867,5307).hashCode());
System.out.println("HashCode of Instance 2 :: "+ new ExtendedPhoneNumber(707,867,5301).hashCode());
Assert.assertEquals(map.get(new ExtendedPhoneNumber(707,867,5301)),"제니");
}
public static class PhoneNumber {
protected int firsrtNumber;
protected int secondeNumber;
protected int thirdNumber;
public PhoneNumber(int firsrtNumber, int secondeNumber, int thirdNumber) {
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof PhoneNumber))
return false;
PhoneNumber p = (PhoneNumber)obj;
return this.firsrtNumber == p.firsrtNumber &&
this.secondeNumber == p.secondeNumber &&
this.thirdNumber == p.thirdNumber;
}
}
public static class ExtendedPhoneNumber extends PhoneNumber {
public ExtendedPhoneNumber(int firsrtNumber, int secondeNumber, int thirdNumber) {
super(firsrtNumber, secondeNumber, thirdNumber);
}
@Override
public int hashCode() {
return 42;
}
}
}
오류가 나지 않는 것을 보면 같은 해쉬코드를 key로 가지면 같은 value를 참조한다는 것을 볼 수 있다.
만일 우리가 ExtendedPhoneNumber 클래스의 인스턴스를 HashMap의 Key값으로 사용한다면 hashCode 메소드를 재정의 해야한다.
재정의 시 "똑같은 객체는 똑같은 hashCode를 리턴한다" 라는 기준을 생각하면서 정의하자!
@Override
public final int hashCode() {
int result = 17;
if (city != null) {
result = 31 * result + city.hashCode();
}
if (department != null) {
result = 31 * result + department.hashCode();
}
return result;
}
https://www.baeldung.com/java-hashcode
https://www.baeldung.com/java-equals-hashcode-contracts
https://www.techiedelight.com/override-equals-hashcode-method-java/