SseopE
SseopE
SseopE
전체 방문자
오늘
어제
  • 분류 전체보기 (44)
    • Programming (3)
      • JAVA (3)
    • Spring (7)
      • 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 (5)
      • Spring 공부 (0)
    • Infra (6)
      • Docker (3)
      • Kubernetes (3)
      • Kafka (0)
    • Machine Learning (2)
      • Scikit-Learn (1)
      • MLOps (1)
      • BentoML (0)
      • Kubeflow (0)
    • OS (2)
      • Linux (2)
    • Algorithm (23)
      • Sorting (7)
      • BOJ (15)
      • Programmers (0)
      • Data Structure (1)
    • 특강 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 1931번
  • Spring boot
  • 2981번
  • Spring
  • docker
  • 1541번
  • java
  • 2580번
  • container
  • 스웜 모드
  • 자바
  • TransformMixin
  • 도커
  • boj
  • 2275번
  • Kubernetes
  • scikit learn
  • BaseEstimator
  • 백준
  • resource

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
SseopE
Programming/JAVA

Java Equals 정의시 getClass() or instanceof

Programming/JAVA

Java Equals 정의시 getClass() or instanceof

2023. 1. 2. 16:17

이펙티브 자바 아이템 10을 읽던 중 의문점이 하나 생겼다.

equals를 재정의 하는 다양한 방법들이 있는데 그 중에는 같은 클래스인지 확인하기 위해 getClass 를 사용하는 경우와 instanceof를 사용하는 경우가 있었다.

책에서는 getClass를 통해 먼저 클래스를 확인하고 이후 값을 비교하게 되면 Liskov의 원칙을 위배하게 된다고 한다.

하지만 IDE 에서 자동 equals 생성을 할 시

class Person {

    String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Person person = (Person) o;
        return Objects.equals(name, person.name);
    }
}

이처럼 getClass를 통해서 확인하도록 만들어져 있다. 그렇다면 이건 잘못된 것인가? 궁금해졌다.

 

Liskov원칙을 위배하게 된점은

"어떤 타입의 중요한 속성이라면 그 하위 타입에서도 마찬가지로 중요하다. 따라서 그 타입의 모든 메서드가 하위 타입에서도 똑같이 잘 작동해야한다." [Liskov87]

라는 점을 위배하게 된 것인데 예를 들어 위를 상속받은 Developer 라는 클래스가 있다고 하자.

class Developer extends Person {

    String job;

    public Developer(String name, String job) {
        super(name);
        this.job = job;
    }
}

이 Developer 는 job라는 직군이 추가된 클래스이다.

Person p = new Person("민수");
Developer d = new Developer("민수", "BE");
System.out.println(p.equals(d)); // false

두 객체를 생성하고 비교하면 이름이 같으면 같은 객체라고 나타내도록한 equals가 정상적으로 작동하지 않는다.

Person을 상속받은 하위타입인 Developer에서 equals 메서드가 작동하지 않은것이다.

 

이러한 점에서 Liskov원칙을 위배할 수 있기에 getClass() 보다 instanceof를 사용한 비교를 하도록 권장하는 것 같다.

class Person {

    String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
    	if (o == this) {
        	return true;
        }
    
        if (!(o instanceof Person)) {
            return false;
        }

		return ((Person) o).name == name;
    }
}

class Developer extends Person {

    String job;

    public Developer(String name, String job) {
        super(name);
        this.job = job;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }

        if (!(o instanceof Developer)) {
            return super.equals(o);
        }

        Developer d = (Developer) o;
        return name == d.name && job == d.job;
    }
}

이렇게 말이다.

 

하지만, 여기서 하나 의문점이 또 생겨났다. 과연 instanceof로 만든 equals는 문제가 없을까 ?

실제로 좀 조사를 해보니 좀 많이 예전이긴 하지만 작가피셜 effetive java 에서 가장 논쟁점이 많은 부분이 이 item10 이라고 한다. 

(https://www.artima.com/articles/josh-bloch-on-design#part17 에서 instanceof versus getClass in equals Methods 항목을 보면 joshua bloch가 인터뷰한 내용을 확인할 수 있다.)

https://stackoverflow.com/questions/596462/any-reason-to-prefer-getclass-over-instanceof-when-generating-equals

Stack Overflow에서도 활발한 토론이 이루어졌다.

 

Bill Venners 는 getClass를 사용한 이유를 구현하고 있는 클래스를 상속받은 하위클래스에서 어떤일이 일어날지 모르기 때문에 상위타입의 equals를 사용하여 비교하는 것은 하위타입에 오히려 맞지 않는다는 입장인것같다.(정확하지 않을 수 있다.....)

 

Joshua Bloch는 Java 의 Collections(List, Map, Set...)의 동작방식이 equals를 이용하기에 getClass를 사용하면 하위타입이 들어맞지 못하는 경우가 발생하기에 instanceof를 통해 같도록 만들어서 Liskov원칙을 위배하지 않도록 하는게 더 낫다는 입장인 것 같다.

 

둘다 정확히 정답은 없는 것 같다.

 

객체 설계를 잘 한다면 중요한 필드를 통해 상위, 하위 관계없이 비교가 잘 되도록 필드 구성을 할 수 있기에 instanceof를 쓰면 좋을 것 같긴하지만 하위 클래스가 개념적으로 틀어지는 경우에는 상위 클래스의 equals를 사용하는 것이 정말 맞는지, 동등하게 결과가 나오는 게 맞는지를 따져서 객체의 비즈니스적인 성격에 따라 변경될 것 같다. 그럴때는 getClass를 통해 다른 객체로 인식되도록 하는 것이 좋지 않을까...(객체 설계가 잘못됬을 수도 있을 것 같다.)

 

'Programming > JAVA' 카테고리의 다른 글

Java HashSet에서 HashCode를 변경하게 되면...  (0) 2023.01.03
Objects requireNonNull 란?  (0) 2022.12.30
    'Programming/JAVA' 카테고리의 다른 글
    • Java HashSet에서 HashCode를 변경하게 되면...
    • Objects requireNonNull 란?
    SseopE
    SseopE

    티스토리툴바

    단축키

    내 블로그

    내 블로그 - 관리자 홈 전환
    Q
    Q
    새 글 쓰기
    W
    W

    블로그 게시글

    글 수정 (권한 있는 경우)
    E
    E
    댓글 영역으로 이동
    C
    C

    모든 영역

    이 페이지의 URL 복사
    S
    S
    맨 위로 이동
    T
    T
    티스토리 홈 이동
    H
    H
    단축키 안내
    Shift + /
    ⇧ + /

    * 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.