코틀린의 Any에는 다음과 같이 잘 설정된 규약들을 가진 메서드들이 있습니다.
- equals
- hashCode
- toString
이 메서드들은 자바 때부터 정의되어 있던 메서드라서 코틀린에서 중요한 위치를 차지하고 있으며, 수 많은 객체와 함수들이 이 규약에 의존하고 있습니다. 따라서 규약을 위반하면, 일부 객체 또는 기능이 제대로 동작하지 않을 수도 있습니다.
동등성
코틀린에는 두 가지 종류의 동등성이 있습니다.
- 구조적 동등성: equals 메서드와 이를 기반으로 만드어진 == 연산자 (!= 포함)
- 레퍼런스적 동등성: ===연산자 (!== 포함), 두 피연산자가 같은 객체를 가르키면, true를 리턴합니다.
둘이 상속 관계를 갖는 경우에는 equals 연산자를 사용하여 비교가 가능하지만, 다른 타입일 경우에는 비교가 불가능합니다.
equals가 필요한 이유
Any클래스에 구현되어 있는 equals 메서드는 디폴트로 ===처럼 두 인스턴스가 완전히 같은 객체인지를 비교합니다. 이는 모든 객체는 디폴트로 유일한 객체라는 것을 의미합니다.
하지만 동등성을 약간 다른 형태로 표현해야 하는 객체가 있습니다. 예를 들어 두 객체가 기본 생성자의 프로퍼티가 같다면, 같은 객체로 보는 형태가 있을 수 있습니다. 이 경우에는 data 한정자를 붙여서 데이터 클래스로 정희하면, 자동으로 이와 같은 동등성으로 동작합니다.
데이터 클래스는 내부의 어떤 값을 갖고 있는지가 중요하므로, 이와 같이 동작하는 것이 좋습니다.
데이터 클래스의 동등성은 모든 프로퍼티가 아니라 일부 프로퍼티만 비교해야 할 때도 유용합니다. 왜냐하면 기본 생성자에 선언되지 않은 프로퍼티는 copy로 복사되지 않기 때문입니다.
equals를 직접 구현해야 하는 경우를 정리해 보면 다음과 같습니다.
- 기본적으로 제공되는 동작과 다른 동작을 해야 하는 경우
- 일부 프로퍼티만으로 비교해야 하는 경우
- data 한정자를 붙이는 것을 원하지 않거나, 비교해야 하는 프로퍼티가 기본 생성자에 없는 경우
equals의 규약
규약은 어떤 다른 객체가 이 객체와 '같은지(equal to)' 확인할 때 사용할 수 있습니다. 구현은 반드시 다음과 같은 요구 사항들을 충족해야 합니다.
- 반사적(reflexive) 동작: x가 null이 아닌 값이라면, x.equals(x)는 true를 리턴해야 합니다.
- 대칭적(symmetric) 동작: x와 y가 null이 아닌 값이라면, x.equals(y)는 y.equals(x)와 같은 결과를 추력해야 합니다.
- 연속적(transitive) 동작: x, y, z가 null이 아닌 값이고, x.equals(y)와 y.equals(z)가 true 라면 x.equals(z)도 true여야 한다.
- 일관적(consistent) 동작: x와 y가 null이 아닌 값이라면, x.equals(y)는 여러 번 실행하더라도 항상 같은 결과를 리턴해야 한다.
- null과 관련된 동작: x가 null이 아닌 값이라면, x.equals(null)은 항상 false를 리턴해야 한다.
equals 구현하기
특별한 이유가 없다면, 직접 equals를 구현하는 것은 좋지 않습니다. 기본적으로 제공되는 것을 그대로 쓰거나, 데이터 클래스로 만들어서 사용하는 것이 좋습니다. 그래도 직접 구현해야 한다면, 규약을 잘지키고 있는지 확애해야 합니다. 글고 이러한 클래스는 final로 만드는 것이 좋습니다. 만약 상속을 한다면, 서브클래스에서 equals가 동작하는 방식을 변경하면 안된다는 것을 기억해야 합니다. (참고로 상속을 지원하면서도 완벽한 사용자 정의 equals 함수를 만든느 것은 불가능에 가깝습니다.)
'Kotlin' 카테고리의 다른 글
[Effective Kotlin] 42 - compareTo의 규약을 지켜라 (0) | 2023.11.12 |
---|---|
[Effective Kotlin] 41 - hashCode의 규약을 지켜라 (0) | 2023.11.04 |
[Effective Kotlin] 39 - 태그 클래스보다는 클래스 계층을 사용하라 (0) | 2023.10.22 |
[Effective Kotlin] 38 - 연산 또는 액션을 전달할 때는 인터페이스 대신 함수 타입을 사용하라 (0) | 2023.10.14 |
[Effective Kotlin] 37 - 데이터 집합 표현에 data 한정자를 사용하라 (0) | 2023.10.08 |