본문 바로가기
Java

Java - Lombok 주의사항 2 - @EqualAndHashCode

by 오늘부터개발시작 2022. 9. 19.

 

 

 

Lombok 주의사항 2 - @EqualAndHashCode

지난 시간에는 @AllArgsConstructor와 @RequiredArgsConstructor를 사용할 때 주의 사항에 대해서 알아보았다. 오늘은 @EqualAndHashCode를 사용할 때 주의사항에 대해서 알아보도록하겠다.

 

@EqualAndHashCode

@EqualAndHashCode를 사용하면 자동으로 hashCode()와 equals() 메소드를 생성해 준다. equals()는 필드값들을 비교해서 동일한 객체인지 판단하고 hashCode()는 필드값들을 사용해서 해시코드를 생성해주는 메소드이다.

 

@EqualAndHashCode로 문제가 될 수 있는 부분은 hashCode()인데 예시를 통해 어떤 문제가 생길 수 있는지 확인해보자.

(equals()는 필드의 값을 토대로 비교하기 때문에 어노테이션으로 생성해도 문제가 되지 않는다)

 

1. Immutable 필드를 사용해서 만들기

아래와 같은 클래스가 있다고 가정하면 storeId를 제외한 값들은 모두 mutable한 값이다. 아무런 옵션 없이 @EqualAndHashCode를 사용하면 모든 필드를 사용해서 해시코드를 생성하기 때문에 런타임에 openAt이나 closeAt이 수정되면 해시코드도 바뀌게 된다.

 

    @EqualsAndHashCode
    public static class Store {
        public int storeId;
        public int openAt;
        public int closeAt;
        
        
        public int hashCode() {
            int PRIME = true;
            int result = 1;
            result = result * 59 + this.storeId;
            result = result * 59 + this.openAt;
            result = result * 59 + this.closeAt;
            return result;
        }
    }

 

 

해시코드는 HashMap과 같은 자료구조에서 Key 값으로 사용되는데 위와 같은 hashCode()는 openAt, closeAt 같은 필드 값이 변경되면 해시코드도 바뀌게 되어 아래처럼 HashMap에 동일한 객체로 값을 찾을 수 없게 된다.

 

    public static void main(String[] args) {
        Store store = new Store(153132, 1200, 2400);

        Map<Store, String> storeSet = new HashMap<>();
        storeSet.put(store, "hashcode 정상");
	// 변경 전 
        System.out.println(storeSet.getOrDefault(store, "hashcode mutated"));

	// 변경
        store.setCloseAt(2300);
	// 변경 후
        System.out.println(storeSet.getOrDefault(store, "hashcode mutated"));
    }

    // 변경 전 print 결과: hashcode 정상 
    // 변경 후 print 결과: hashcode mutated

 

 

@EqualAndHashCode에는 다행히도 이와 같은 문제점을 해결하기 위해 어떤 필드를 equals()와 hashCode() 메소드에 사용할 것인지 옵션으로 정의해 줄 수 있다. 아래처럼 storeId만 사용해서 메소드를 생성해주면 immutable 값만을 사용했기 때문에 HashMap과 같은 해시코드를 Key를 사용하는 자료구조에서도 문제 없이 사용할 수 있게 된다.

 

	@EqualsAndHashCode(of = {"storeId"})
	public static class Store {
        private int storeId;
        private int openAt;
        private int closeAt;

        public int hashCode() {
            int PRIME = true;
            int result = 1;
            result = result * 59 + this.getStoreId();
            return result;
        }
    }

 

 

2. 필드가 없는 클래스에서 사용 금지

다음과 같이 필드가 하나도 없는 Store 같은 클래스에서 @EqualsAndHashCode를 사용하면 언제나 1을 반환하는 hashCode() 메소드가 생성되게 되므로 이런 경우에는 @EqualsAndHashCode를 사용해서는 안된다.

    @EqualsAndHashCode
    public static class Store {
        public int hashCode() {
            int result = true;
            return 1;
        }
    }