[Java] Enum 사용 시 비교는 == 일까 equals 일까
최근에 회사 동료가 코드 검사를 하겠다며 내가 작성한 코드를 보고 리뷰를 했다.
그러던 중 공통 상수로 선언한 클래스와 Enum 클래스를 사용한 것에 대해 이야기를 하였는데, Enum 을 사용하는데 왜 == 로 비교하지 않고, equals 메소드를 사용해서 비교하는가에 대해 이야기를 하게 되었다.
이 문제에 대해서 간단하게 고민해보고 생각한 것을 포스팅해보려고 한다.
1. 코드 상황
- 간단하게 구현된 코드 상태를 작성해보자면 아래와 같습니다.(이해를 돕기 위한 예제 코드입니다.)
- Controller
@RestController
public class MemberController {
@PostMapping("")
public String memberTest(@RequestBody RequestMember member) {
if(MemberEnum.MEMBER.equals(member.getMemberEnum())) {
...
}
if(MemberEnum.QUIT_MEMBER.equals(member.getMemberEnum())) {
...
}
return null;
}
}
- Enum
public enum MemberEnum {
MEMBER("01"),
QUIT_MEMBER("02"),
TEMP_MEMBER("03")
;
private String code;
MemberEnum(String code) {
this.code = code;
}
}
2. Enum 값을 비교하기(== vs equals)
Java 에서 Enum 은 클래스 인스턴스가 JVM 내에 하나만 존재하도록 100% 보장되는 싱글톤 객체입니다.(JLS, 8.9 Enum Types)
2-1. 공통점
- 간단하게 테스트 코드를 통해서 확인해보겠습니다.
@Test
@DisplayName("Enum 기본 테스트")
void Enum_Test() {
MemberEnum test = MemberEnum.MEMBER;
assertThat(test == MemberEnum.MEMBER).isEqualTo(true);
assertThat(test.equals(MemberEnum.MEMBER)).isEqualTo(true);
assertThat(MemberEnum.MEMBER == test).isEqualTo(true);
assertThat(MemberEnum.MEMBER.equals(test)).isEqualTo(true);
}
- 테스트를 수행해보면 모두 통과하는 것을 확인할 수 있는데, 이는 equals 메소드를 확인해보면 알 수 있습니다.
- 아래 그림에서와 같이 equals 메소드도 결국 == 연산자를 통해서 비교하고 있는 것을 확인할 수 있습니다.
2-2. 차이점
- Null Safe(== 비교는 NPE를 발생시키지 않는다.)
@Test
@DisplayName("Enum 비교 Null 체크 테스트")
void Enum_NPE_Test() {
MemberEnum test = null;
System.out.println(test == MemberEnum.MEMBER);
System.out.println(test.equals(MemberEnum.MEMBER)); // NPE 발생
System.out.println(MemberEnum.MEMBER == test);
System.out.println(MemberEnum.MEMBER.equals(test));
}
- == 비교는 NPE(NullPointerException) 을 발생시키지 않습니다.
- eqauls 메소드를 사용하여 비교하면 Runtime(런타임)에 NPE가 발생할 수 있습니다.(But, equals 메소드에서도 Enum 을 먼저 앞에 두고 비교하면 NPE 에서 벗어날 수 있습니다.)
위에 설명에서와 같이 NPE를 방지하는 것 뿐만 아니라 상수값, Enum 과 같은 변하지 않는 값들을 앞에 작성해서 비교하는 것이 좋습니다.
- == 비교는 Compile(컴파일)에서부터 타입 미스 매치를 잡아준다.(이미지 참고)
@Test
@DisplayName("Enum 비교 Type 체크 테스트")
void Enum_Type_Test() {
System.out.println(BookEnum.BOOK == MemberEnum.MEMBER);
System.out.println(BookEnum.BOOK.equals(MemberEnum.MEMBER));
System.out.println(MemberEnum.MEMBER == BookEnum.BOOK);
System.out.println(MemberEnum.MEMBER.equals(BookEnum.BOOK));
}
- 위의 그림에서 확인하실 수 있듯이, == 비교를 사용하면 실수로 잘못된 타입 비교를 작성하여도 컴파일 과정에서 확인이 되지만 equals 메소드를 사용하게 되면 그대로 컴파일이 되어 버립니다.
3. 마무리
- 극단적이지만, 아래 예제를 확인해보겠습니다.
실제론 아래와 같이 코드를 작성하면 안됩니다!!!
@RestController
public class MemberController {
@PostMapping("")
public String memberTest(@RequestBody RequestMember member) {
if(MemberEnum.MEMBER.name().equals(member.getMemberEnum())) {
...
} else if(MemberEnum.QUIT_MEMBER.name().equals(member.getMemberEnum())) {
...
} else {
// 만약 Delete 코드가 있다면...??
}
return null;
}
}
- Enum 타입과 Enum의 코드를 비교하니 어느 if 에도 걸리지 않습니다.
- Enum을 비교해서 알맞은 if 문을 통해서 로직을 수행하고자 하였지만, 실제론 else 문을 타게 되고 Delete 로직이 수행되면서 중요한 데이터가 삭제되는 경우가 발생할 수도 있습니다.
- 만약 == 비교를 사용하였다면 컴파일 자체가 되지 않으면서 실수를 캐치할 수 있었을 것이라 생각합니다.
- 사실 이 문제는 정답이 딱 정해져있는 문제는 아니라고 생각합니다. 이 문제에 대해서는 스택오버플로우에서도 다양한 이야기를 하고 있습니다.(https://stackoverflow.com/questions/1750435/comparing-java-enum-members-or-equals)
- 그렇지만, 컴파일 단계에서 실수를 확인할 수 있는 == 비교가 조금 더 유용하지 않을까라는 생각을 가지게 되었습니다.
참고