본문 바로가기
개발

[Lombok] @EqualsAndHashCode

by angeloper 2023. 7. 17.
반응형

@EqualsAndHashCode는?

Java 개발에서 equals() 및 hashCode() 메서드에 대한 상용구 코드 작성은 지루하고 오류가 발생하기 쉽습니다. Lombok의 @EqualsAndHashCode 주석은 메서드를 자동으로 생성하여 코드에서 개체 동등성을 보다 쉽게 ​​설정할 수 있도록 하는 편리함을 제공하고 있습니다.

사용법

Java class 위에 @EqualsAndHashCode 주석을 붙여서 사용할 수 있습니다.

import lombok.EqualsAndHashCode;

@EqualsAndHashCode
public class Person {
    private String name;
    private int age;
}

상단의 코드를 확인할 경우, 다음과 같이 코드가 생성됩니다. 보시다시피, equals, canEqual, hashCode의 함수가 생성된 부분을 확인할 수 있으며, equals와 canEqual의 boolean으로 hashCode는 int로 return 되는 것을 확인하실 수 있습니다.

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof Person)) {
            return false;
        } else {
            Person other = (Person)o;
            if (!other.canEqual(this)) {
                return false;
            } else if (this.age != other.age) {
                return false;
            } else {
                Object this$name = this.name;
                Object other$name = other.name;
                if (this$name == null) {
                    if (other$name != null) {
                        return false;
                    }
                } else if (!this$name.equals(other$name)) {
                    return false;
                }

                return true;
            }
        }
    }

    protected boolean canEqual(final Object other) {
        return other instanceof Person;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        result = result * 59 + this.age;
        Object $name = this.name;
        result = result * 59 + ($name == null ? 43 : $name.hashCode());
        return result;
    }
}

@EqualsAndHashCode의 추가 옵션

EqualsAndHashCode 또한 괄호 안에 추가적인 옵션을 사용하실 수 있습니다. 그럼 추가적인 옵션의 종류가 무엇이 있는지 확인해 보도록 하겠습니다.

  • exclude : 외부 필드의 속성값을 비교에 포함시키지 않고 비교를 진행하여는 경우, 이 옵션을 사용하여 해당 필드를 명시합니다.
  • callSuper :  종류는 true와 false를 사용할 수 있으며, 기본값은 false입니다. true로 설정할 시, Object 클래스를 상속받을 때 상위 클래스의 equals()와 hashCode() 메서드를 호출합니다.
  • doNotUseGetters : 종류는 true와 false를 사용할 수 있으며, 기본값은 false입니다. true로 설정하면, getter 메서드를 사용하지 않고 필드 접근을 직접 이용하여 equals()와 hashCode() 메서드를 생성합니다.
  • cacheStrategy : 종류는 NAVER와 LAZY를 사용할 수 있으며, 기본값은 NAVER입니다.
  • onParam : 메서드 파라미터에 추가할 어노테이션입니다. 이러한 에노테이션은 생성된 메서드들의 파라미터로 전달합니다.
  • onlyExplicitlyIncluded : 종류는 true와 false를 사용할 수 있으며, 기본값은 false입니다. true로 설정하면, 명시적으로 어노테이션이 붙은 필드들만 비교를 대상으로 합니다.

예제코드

import lombok.EqualsAndHashCode;

@EqualsAndHashCode(onlyExplicitlyIncluded = true)
class MyClass {
    @EqualsAndHashCode.Include
    private Long id;

    private String className;
}

위 코드를 자세히 보자면 다음과 같이 되어 있습니다. (중요)위에 예제를 든 이유를 말씀드리자면, 보통 데이터베이스에서 같은 값인지 확인하기 위해서 모든 값을 비교할 필요가 없이 Primary Key만 같다면 내용이 같은 거라고 판단하실 수 있으실 겁니다. (단, 값을 수정할 시에는 제외) 이때, 조회한 내용과 수정한 내용의 Key가 같은 것을 확인하기 위해서 객체의 주소값으로 비교하거나 각각의 Primary Key의 값을 비교하시는 분들이 있으실 텐데, 이 기능을 사용하면 더 쉬운 방법으로 사용할 수 있다고 생각하여 하기 사항을 예제로 들었습니다.

class MyClass {
    private Long id;
    private String className;

    MyClass() {
    }

    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof MyClass)) {
            return false;
        } else {
            MyClass other = (MyClass)o;
            if (!other.canEqual(this)) {
                return false;
            } else {
                Object this$id = this.id;
                Object other$id = other.id;
                if (this$id == null) {
                    if (other$id != null) {
                        return false;
                    }
                } else if (!this$id.equals(other$id)) {
                    return false;
                }

                return true;
            }
        }
    }

    protected boolean canEqual(final Object other) {
        return other instanceof MyClass;
    }

    public int hashCode() {
        int PRIME = true;
        int result = 1;
        Object $id = this.id;
        result = result * 59 + ($id == null ? 43 : $id.hashCode());
        return result;
    }
}

@EqualsAndHashCode의 장점

  • 코드 간결성: equals()와 hashCode() 메서드를 수동으로 작성할 필요가 없기 때문에 클린하고 간결한 코드를 유지할 수 있습니다.
  • 유지 보수성: 어노테이션 옵션을 사용하여 비교할 필드를 선언하거나 배제하는 것이 쉽습니다. 필드가 추가/제거 될 경우 코드를 변경할 필요 없이 해당 어노테이션을 업데이트하면 됩니다.
  • 실수 방지: equals()와 hashCode() 메서드를 수동으로 구현하면서 발생할 수 있는 실수를 방지하여 안정적인 코드를 생성할 수 있습니다.
  • 일관성: @EqualsAndHashCode 어노테이션을 사용하여 구현하는 것은 이들 메서드를 작성하는 표준화된 방법으로 팀 내 일관성을 높입니다.

@EqualsAndHashCode의 단점

  • 런타임 의존성: 롬복 사용할 수 있게 런타임 의존성을 갖게 되면, 롬복 라이브러리를 포함하여 프로젝트를 배포하여야 합니다.
  • 가독성: @EqualsAndHashCode와 같은 롬복 어노테이션은 명시적이지 않을 수 있기 때문에 코드를 처음 보는 개발자들에게 이해하기 어려울 수 있습니다. 코드 리뷰 단계에서도 주의할 필요가 있습니다.
  • IDE 및 빌드 도구 호환성: 롬복은 일부 IDE 및 빌드 도구와의 통합 문제가 발생할 수 있으며, 추가 설정이 필요할 수도 있습니다.
  • 커스터마이징 제한: 롬복 어노테이션의 기본 동작은 원하는 결과를 얻지 못할 경우, 해당 기능을 수동으로 구현하거나 어노테이션 설정을 변경해야 합니다.
반응형