동등성과 동일성에 대해

equals 메소드는?

동등성을 비교할 때 사용하는 메소드로 자바의 최상위 클래스인 Object 클래스에 정의되어 있다.

동등성이란?

동등성을 이해하기 전에 동일성에 대해 알아보자.

무언가 동일하다는 것은 물리적으로 같은 것임을 의미한다. 예를 들어 서울에 있는 나와 다른 시간 대에 부산에 있는 나는 '동일하다'고 할 수 있다. 그렇다면 동등성은 동일성과 무엇이 다를까?

 

동등성은 논리적인 같음을 의미한다. 예를 들어 어제와 오늘은 날짜도 다르고 물리적으로 별개이다. 그러나 1년 365일 중 하루라는 논리적 의미로는 어제나 오늘이다 같다. 즉 동등성은 실제 물리적 속성을 비교하면 다르지만 논리적인 개념으로 같은 성질을 가질 때를 의미한다.

 

프로그래밍적 관점에서는 서로 다른 변수가 같은 메모리 주소를 가지면 동일하다고 할 수 있고, 서로 다른 변수가 다른 메모리 주소를 가지지만 변수가 의미하는 객체의 속성(이름, 주소 등)이 같다면 동등하다고 할 수 있다. 동일성은 == 으로 동등성은 equals 메소드로 확인 가능하다.

Object 클래스의 equals는 어떻게 구현되어 있을까?

public boolean equals(Object obj) {
        return (this == obj);
}

 

위 코드에서 확인할 수 있듯이 Object 클래스의 equals메소드는 == 연산을 통한 비교만 수행한다. 즉 인자로 넘어온 변수가 비교대상과 같은 메모리 주소를 가지는지를 확인한다. 즉 동일성 비교이다.

그럼 동등성 비교는 어떻게?

모든 클래스는 Object 클래스를 상속 받으므로 equals 메소드를 오버라이딩 할 수 있다.

public class Apple {

    private String name;
    private int price;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Apple)) return false;
        Apple apple = (Apple) o;
        return price == apple.price && Objects.equals(name, apple.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }
}

 

예를 들어 Apple이라는 클래스가 있고 instance 변수로 name, price가 있을 때 name과 price가 같다면 동등하다고 정의하고 싶은 상황이다. 그럼 위와 같이 메모리 주소가 같다면 애초에 동일하기 떄문에 true 반환, 인자로 넘어온 객체가 price가 같고 name이 같으면 true를 반환하도록 재정의하면 된다. 인자로 넘어온 객체가 Apple 클래스의 객체가 아니라면 당연히 false를 반환하도록 구현한다. 이와 같은 코드는 대부분의 IDE에서 자동생성 기능을 제공하기 때문에 개발자가 직접 구현할 필요는 없다.

hashCode 메서드는 무엇?

위 코드에서 뜬금없이 hashCode 메소드가 나왔다. IDE에서 equals 메소드를 재정의 하려고하면 동시에 hashCode도 재정의하라고 할 것이다. 왜 그럴까?

예를 들어 자바에서 제공하는 Set 자료구조에 객체를 할당하는 상황이라고 해보자

Apple a = new Apple("apple", 1000);
Apple b = new Apple("apple", 1000);

Set<Apple> set = new HashSet<>();

 

a, b는 equals 메소드를 재정의 했기 때문에 동등한 객체이다. 그런데 hashCode를 재정의 하지 않은 상황에서 set의 갯수는 1개로 보장할 수 있을까? Set 자료구조는 중복을 허용하지 않는 것인데 말이다. 실제 코드를 테스트해보면 예상과 다르게 set의 갯수는 2개로 나올 것이다.

 

이는 HashSet은 내부적으로 해시테이블을 사용해 객체를 관리하는데 이 때 객체를 구분하는 기준이 hashCode값이기 때문에 발생하는 것이다. hashCode는 메모리 주소를 16진수의 정수로 반환하는 메소드이다. 즉 new 로 객체를 생성하면 서로 다른 메모리 주소를 할당받으므로 hashCode를 재정의 하지 않으면 서로 다른 hashCode를 반환하므로 다른 객체로 인식되는 것이다. 따라서 Apple 클래스 소스코드에서 확인할 수 있듯이 name, price가 같으면 같은 hashCode를 반환하도록 재정의하는 것이다.

 

f-lab 멘토링 과정에서 equals메소드를 재정의하지 않은 상태에서 서로 다른 변수 예를 들어, a.equals(b) 를 했을 때 true가 나올 것인지에 대한 질문에 정확한 답을 하지못하여 정리하는 차원에서 작성해보았다.

'Java' 카테고리의 다른 글

서블릿 컨텍스트  (0) 2023.12.06
인터페이스와 추상 클래스  (0) 2023.11.30
String의 equals와 compareTo 메서드 비교  (0) 2023.11.30
JVM 구조와 메모리  (0) 2023.11.30
JAVA에서 정렬하는 방법  (0) 2023.11.30