Kotlin에서는 java의 object와 같이 모든 객체는 Any를 상속받는다.
이 Any에는 몇가지 잘 설계된 contract들이 있다.
- equals
- hashCode
- toString
Equality
Kotlin에서는 2가지의 Equality가 있다.
- Structural equality
equals method나 == 연산자를 통해 체크가 된다. a == b는 a.equals(b)와 같다. (nullable이면 a?.equals(b) ?: (b === null)
- Referential equality
=== 연산자를 통해 체크된다. 양 쪽이 같은 오브젝트를 가리킬 때만 true를 return한다.
equals는 Any에 구현되어 있기 때문에 모든 객체에서 사용가능하지만 다른 타입의 객체를 비교할 때는 사용이 불가능하다.
open class Animal
class Book
Animal() == Book() // error
Animal() === Book() // error
class Cat: Animal()
Animal() == Cat() // OK
Animal() === Cat() // OK
Equals가 필요한 이유
default로 구현되어 있는 것은 referential equality(===)같이 다른 오브젝트가 정확하게 같은 인스턴스인지를 체크하는 것이다.
class Name(val name:String)
val name1 = Name("Marcin")
val name2 = Name("Marcin")
val name1Ref = name1
name1 == name1 // true
name1 == name2 // false
name1 == name1Ref // true
name1 === name1 // true
name1 === name2 // false
name1 === name1Ref // true
data 클래스에서는 생성자에 포함된 모든 property들이 같으면 true를 반환한다.
data class FullName(val name: String, val surname: String)
val name1 = FullName("Marcin", "Moskała")
val name2 = FullName("Marcin", "Moskała")
val name3 = FullName("Maja", "Moskała")
name1 == name1 // true
name1 == name2 // true, because data are the same
name1 == name3 // false
name1 === name1 // true
name1 === name2 // false
name1 === name3 // false
모든 property를 비교하고 싶지 않을 때는 다음과 같이 구현하면 된다.
data class로 하면 비교하고 싶지 않은 property는 생성자에 넣지 않으면 된다.
이 경우, copy할 때 기본 생성자에 포함되지 않은 property들은 복사되지 않는다.
따라서, 우리가 equals를 직접 구현해야할 때는 다음과 같다.
- 로직이 default 값과 달라야할 때
- property의 subset만 비교하고 싶을 때
- 오브젝트를 data class로 만들고 싶지 않거나 기본 생성자에 포함되지 않은 값을 비교해야할 때
equals를 직접 구현할 때는 다음과 같은 성질을 만족해야한다.
- Reflexive: non-null value x에 대해 x.equals(x)는 항상 성립
- Symmetric: non-null value x, y에 대해 x.equals(y)가 참이면 y.equals(x)도 참이어야 한다.
- Transitive: non-null value x, y, z에 대해 x.equals(y), y.equals(z)가 참이면 x.equals(z)도 참이어야한다.
- Consistent: non-null value x, y에 대해 x.equals(y)를 여러 번 불러도 데이터가 바뀌지 않았다면 결과가 일관적이어야 한다.
- Never equal to null: non-null value x는 null과 항상 같지 않다. x.equals(null)은 false.
추가적으로 equals, toString, hashCode는 빨라야한다.
이 성질이 깨지면 다른 오브젝트에서 비교를 할 때, x to y로 비교할지 y to x로 비교할지 모르기 때문에 정상적으로 동작하지 않을 수 있다.
java.net.URL
이건 true일까 false 일까?
인터넷이 켜져 있으면 True, 꺼져있으면 False 라고 한다..!
안드로이드에서는 4.0부터 수정이 되었다.
이러한 문제 때문에 Kotlin/JVM을 사용하면 java.net.URL 대신 java.net.URI를 사용하도록 권장한다.
'개발 > Kotlin' 카테고리의 다른 글
Item 49. Consider using inline classes (0) | 2021.11.04 |
---|---|
Item 35: Consider defining a DSL for complex object creation (0) | 2021.09.23 |
Item 28: Specify API stability (0) | 2021.09.16 |
Item 21: Use property delegation to extract common property patterns (0) | 2021.09.02 |