VO사용 시 HashCode를 오버라이딩 해야 하는 이유 (feat. DTO vs VO)

김형진·2023년 7월 26일
0

DTO는 layer 간 데이터 전달 객체, VO는 값을 표현하는 객체임을 알고 개발에서 그렇게 사용한다.
그러나 속성에 의해 생기는 사용법의 차이와 그 이유를 대충 알았어서 글로 정리해본다.

DTO

DTO는 단순히 데이터를 전달하는 역할만 수행해야 한다.
따라서 Getter, Setter 이외의 로직이 있어선 안된다.
(엔티티를 받아서 DTO로 변환하는 생성자가 있는 정도? 그러나 이 역시 로직이라고 보기 어려운 것 같다.)


VO

VO를 사용할 때 기억해야 할 세 가지가 있다.

  1. VO는 값을 표현하는 역할만을 수행한다. 그렇기 때문에 값의 안정성을 보장받기 위해 일반적으로 생성자를 통해서만 속성 값을 주입하고 setter를 통한 값 주입은 구현되지 않는다.

  2. VO는 값을 표현하는 객체이므로 실제 객체가 달라도 값이 같다면 같아야 한다.
    따라서 값이 같은 경우 equals true가 되도록 overriding 해야 한다.

  3. hashMap, hashSet 등 hashCode를 사용하는 경우를 고려하여 hashCode 메소드를 오버라이딩한다.

hashCode를 오버라이딩 해야 하는 이유

상황 예시를 통해 이유를 알아보자.

    @Getter
    public static class ExampleVO{
        private final int value;
        public ExampleVO(int value){
            this.value = value;
        }

        @Override
        public boolean equals(Object other){
            if (this == other)
                return true;
            if (other == null || getClass() != other.getClass())
                return false;
            return this.value == ((ExampleVO)other).getValue();
        }

    }
    
    public static void main(String[] args){
        ExampleVO vo1 = new ExampleVO(1);
        ExampleVO vo2 = new ExampleVO(1);
        System.out.println(vo1.equals(vo2));

        HashSet<ExampleVO> set = new HashSet<>();
        set.add(vo1);
        set.add(vo2);
        System.out.println(set.size());
    }

위 코드 예시를 보면
main에서는 서로 다른 VO객체를 생성했지만 VO객체 내에서 equals 메소드를 오버라이딩했기 때문에
같은 값을 가진 두 객체는 같다고 판별될 것이며, HashSet에 넣은 두 객체는 중복제거가 될 것 같다.

그러나,

실행결과

true
2

equals를 통한 비교는 두 객체가 같다고 하지만, 실제 set에 들어간 객체는 중복 제거되지 않았다. HashSet은 hashCode까지 사용하여 두 값이 같은지 판별하기 때문이다.

따라서 다음과 같이 hashCode를 재정의해줘야 HashSet에서 역시 두 객체가 같음을 판별하고 중복제거할 수 있다.

    public static class ExampleVO{
    
        private final int value;
        public ExampleVO(int value){
            this.value = value;
        }

        @Override
        public int hashCode(){
            return Objects.hashCode(value);
        }

        @Override
        public boolean equals(Object other){
            if (this == other)
                return true;
            if (other == null || getClass() != other.getClass())
                return false;
            return this.value == ((ExampleVO)other).getValue();
        }
        
        public int getValue(){
        	return value;
        }

    }
실행결과

true
1

Lombok을 이용한 구현

    @Getter
    @AllArgsConstructor
    @EqualsAndHashCode(of={"value"})
    public static class ExampleVO{
        private final int value;
    }

profile
히히

0개의 댓글