JAVA 값 객체의 동등 비교 ('==' 과 equals() 의 차이)

2025. 3. 8. 18:35·WEB(BE)/Spring & Spring Boot
반응형

개발하다가 코드 리뷰를 받으면서 들었던 의문점

엔티티 필드에 값 객체가 존재하고, 외부에서 값 객체가 주어질 때, 해당 값 객체의 값과 엔티티가 갖고있는 값 객체의 값이 같은지 판별해야 하는 메서드를 작성할 일이 생겼다.

 

나는 단순하게 값 객체의 필드를 하나 하나 꺼내서 비교하는 메서드를 그냥 작성했는데, 리뷰로는 `equals()` 메서드를 사용하면 된다는 리뷰를 받았다.

 

코드를 작성할 땐 막연히 `equals()를 사용하면 참조비교하겠지?` 정도로만 생각해서 하나씩 세부 필드를 꺼내서 비교하는 코드를 작성했는데, 그 차이점을 직접 비교해보려고 한다.

 

GPT 가 알려준 것

GPT는 값 객체를 비교할 때 equals 메서드를 오버라이딩 하는 것을 추천하고 있다.

구글링 해봤을 때도 다른 블로그들이 모두 비슷하게 말하고 있었다.

 

 

 

기본적으로 equals 메서드는 Object 의 구현을 따라 동일성을 판별한다고 한다.

동일성은 정말 두 객체가 '동일한 객체인지' 판별하는 것으로, 객체의 교유 해시값을 비교한다.

따라서 만약 equals 메서드의 호출 결과가 true 라면 두 객체의 해시코드 값은 같다.

 

 

자바 공식문서를 보면 정말 그렇다고 한다.

 

 

코드로 확인하기

@Embeddable
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public static class Semester {
    private Integer year;
    private SemesterType type;

    public static Semester of(Integer year, SemesterType type) {
        return new Semester(year, type);
    }
}

public enum SemesterType {
    FIRST, SECOND
}

 

위와 같은 값 객체를 만들었다.

혹시 jpa 의 @Embeddable 어노테이션이 비교에 영향을 줄까 싶어 함께 사용했는데, 영향은 따로 주지 않았다.

 

@Test
void testObject() {
    Semester semester1 = new Semester(2024, SemesterType.FIRST);
    Semester semester2 = new Semester(2024, SemesterType.FIRST);

    Assertions.assertThat(semester1.equals(semester2)).isTrue();
}

 

그리고 테스트 코드를 작성했다.

내가 원하는 것은 값이 같다면 객체가 달라도 괜찮은 '동등성' 비교이다.

 

 

테스트는 실패한다.

따라서 기본적으로 equals() 메서드는 객체의 '동일성' 을 판별하는 메서드임을 알 수 있다.

그래서 GPT는 equals() 메서드를 오버라이딩 하라고 한 것이다.

 

@Test
void testObject2() {
    Semester semester1 = new Semester(2024, SemesterType.FIRST);
    Semester semester2 = new Semester(2024, SemesterType.FIRST);

    Assertions.assertThat(semester1 == semester2).isTrue();
}

 

 

이번엔 == 연산자를 사용해서 비교해보았다.

객체에 대해 == 연산자를 사용한 비교를 적용하니 인텔리제이에서 경고문구와 함께 equals() 메서드를 사용하라고 했다.

실행결과는 False 가 나오면서 테스트가 역시 실패했다.

즉, equals() 와 == 연산자 모두 객체에 대해서는 해시코드를 비교하는 '동일성' 비교를 수행한다.

 

그렇다면 '동등성' 비교를 위해서 값을 하나하나 비교하는 메서드를 오버라이딩 해야 할까?

다행히 롬복의 @EqualsAndHashCode 어노테이션을 이용하면 간단하게 해결할 수 있었다.

그리고 equals() 메서드를 오버라이딩 할 때는 객체의 해시값을 계산하는 hashcode() 메서드 역시 함께 오버라이딩 하는 것을 권장하고 있는데, 이 역시 함께 해결된다.

 

 

equals() 메서드 API 문서를 보면 hashCode 메서드도 함께 오버라이딩할 것을 권고하고 있다.

 

@Embeddable
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode
@AllArgsConstructor
public static class Semester {
    private Integer year;
    private SemesterType type;

    public static Semester of(Integer year, SemesterType type) {
        return new Semester(year, type);
    }
}

 

이렇게 롬복 어노테이션을 통해 equals 메서드를 오버라이딩 하였다.

 

 

기존의 equals 메서드를 사용한 비교 테스트가 통과하는 것을 알 수 있다.

하지만 여전히 == 연산자를 사용한 비교 테스트는 실패한다.

== 연산자는 자바의 기본 연산자로서 그 동작 자체가 이미 객체의 해시값 비교로 이루어져 있어 오버라이딩에 영향을 받지 않기 때문이다.

 

그래서 누군가 equals() 메서드와 == 연산자의 차이를 묻는다면 '기본적으로 두 기능의 동작은 같지만, equals 메서드는 오버라이딩하여 그 동작을 재정의할 수 있는 반면, == 연산자는 재정의할 수 없다' 고 말하면 될 것 같다.

 

'연산자 오버로딩'과 '오버라이딩 vs 오버로딩'

그런데 여기까지 왔더니 다시 떠오른 의문점 하나

c++은 기본 연산자 역시 '연산자 재정의' 가 가능한데, 과연 자바도 가능할까?

 

 

 

그리고 여기에서 나온김에 항상 헷갈리는 개념을 다시 정리하자면.

연산자 재정의는 연산자 '오버로딩' 이라고 한다.

 

오버라이딩 = 부모 클래스의 동작을 자식 클래스에서 재정의 하는 것 (상속 개념과 연관)

오버로딩 = 하나의 클래스에서 같은 이름의 메서드를 다른 형태로 여러 개 정의하는 것 (상속 개념과 무관)

 

그렇다면 왜 연산자는 오버로딩인가?

내가 이해한 것은 c++ 에서 + 연산이 자바와 같이 기본 Object 클래스에 구현되어 있는데, 이를 재정의하고 하는 것이 아니라, 애초부터 객체에 대해서는 + 연산이 존재하지 않았는데, 연산자 정의를 통해서 신규로 생성하며 확장하는 느낌이기 때문에 오버로딩이라고 부른다고 이해했다.

 


결론

equals(), == 연산 모두 기본적으로는 객체의 해시값을 비교해서 '동일성' 을 체크한다.

다만 equals() 메서드를 오버라이딩 해서 '동등성' 을 체크하도록 변경할 수 있으며, 이 변경은 == 연산에는 영향을 주지 않는다.

반응형
저작자표시 비영리 변경금지 (새창열림)

'WEB(BE) > Spring & Spring Boot' 카테고리의 다른 글

@SQLDelete와 TransientObjectException 예외  (0) 2026.03.08
[Redis] READONLY You can't write against a read only replica. 에러 해결기 (feat. 중국)  (0) 2025.08.06
[Swagger] Failed to load API definition (403, 500, NoSuchMethodError)  (3) 2025.01.22
[Spring Boot] application.yml 데이터베이스 연결 정보 입력  (0) 2025.01.06
[Spring Boot] profile 개념과 profile 분리  (0) 2025.01.04
'WEB(BE)/Spring & Spring Boot' 카테고리의 다른 글
  • @SQLDelete와 TransientObjectException 예외
  • [Redis] READONLY You can't write against a read only replica. 에러 해결기 (feat. 중국)
  • [Swagger] Failed to load API definition (403, 500, NoSuchMethodError)
  • [Spring Boot] application.yml 데이터베이스 연결 정보 입력
에버듀
에버듀
개발은 좋은데 뭘로 개발할까
  • 에버듀
    Blog. 에버듀
    에버듀
  • 전체
    오늘
    어제
    • 분류 전체보기 (615)
      • 개인 프로젝트 (43)
        • 토이 프로젝트 (3)
        • [2020] 카카오톡 봇 (9)
        • [2021] 코드악보 공유APP (22)
        • [2022] 유튜브 뮤직 클론코딩 (9)
        • [2025] 한글 SQL 데이터베이스 (0)
      • 팀 프로젝트 (22)
        • [2020] 인공지능 숫자야구 (4)
        • [2022] OSAM 온라인 해커톤 (10)
        • [2024] GDSC 프로젝트 트랙 (6)
        • [2025] 큰소리 웹 페이지 (2)
      • CS (335)
        • 자료구조 (19)
        • 어셈블리 (41)
        • 멀티미디어응용수학 (7)
        • 컴퓨터 구조 (29)
        • 알고리즘 분석 (4)
        • 컴퓨터 네트워크 (38)
        • 프로그래밍언어론 (15)
        • HCI 윈도우즈프로그래밍 (26)
        • 기초데이터베이스 (29)
        • 운영체제 (23)
        • 오토마타 (24)
        • 문제해결기법 (11)
        • 블록체인 (22)
        • 소프트웨어공학 (21)
        • 기계학습심화 (12)
        • 컴퓨터그래픽스와 메타버스 (8)
        • 분산시스템특론 (6)
      • 자기계발 (45)
        • 생각 정리 (23)
        • 대외활동 (11)
        • 동아리 (7)
        • 자격증 (3)
        • 머니 스터디 (1)
      • 알고리즘 (PS) (107)
        • BOJ (101)
        • Programmers (5)
        • 알고리즘 이모저모 (1)
      • WEB(BE) (8)
        • express.js (1)
        • Spring & Spring Boot (7)
      • WEB(FE) (2)
        • html, css, js (1)
        • React.js (1)
      • Tool & Language (6)
        • Edit Plus (1)
        • Git (1)
        • Python3 (2)
        • Java (2)
      • Infra (12)
        • AWS (1)
        • Oracle Cloud (8)
        • Firebase (2)
        • Network (1)
      • Android (18)
        • Java (6)
        • Flutter (12)
      • Window (2)
        • Visual Studio 없이 WPF (1)
        • MFC (1)
      • 독서 (14)
        • Inside Javascript (7)
        • Database Internals (6)
        • 한 글 후기 (1)
  • 링크

    • github
    • website
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
에버듀
JAVA 값 객체의 동등 비교 ('==' 과 equals() 의 차이)
상단으로

티스토리툴바