@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 및 빌드 도구와의 통합 문제가 발생할 수 있으며, 추가 설정이 필요할 수도 있습니다.
- 커스터마이징 제한: 롬복 어노테이션의 기본 동작은 원하는 결과를 얻지 못할 경우, 해당 기능을 수동으로 구현하거나 어노테이션 설정을 변경해야 합니다.