class Client(val name: String, val postalCode: Int){
override fun toString() = "Client(name=${name}, postalCode=${postalCode})"
}
>>> val client = Client("홍길동", 2022)
>>> println(client)
Client(name=홍길동, postalCode=2022)
==
연산자는 참조 동일성을 검사하지 않고 객체의 동등성을 검사한다. 따라서 ==
연산은 equals를 호출하는 식으로 컴파일 된다. 참조 비교를 위해서는 ===
연산자를 사용한다.>>> val client1 = Client("홍길동", 1111)
>>> val client2 = Client("홍길동", 1111)
>>> println(client1 == client2)
false
Client 클래스의 객체 동등성을 만족시키려면 equals를 오버라이드 해야 한다.
class Client(val name: String, val postalCode: Int) {
override fun equals(other: Any?): Boolean {
if (other == null || other !is Client)
return false
return name == other.name && postalCode == other.postalCode
}
}
hashCode 규약hash code란
객체를 식별하는 하나의 정수 값. hashCode() 메소드는 객체의 메모리 번지를 이용해서 해시코드를 만들기 때문에 객체마다 다른 값을 가지고 있다.
HashSet
, HashMap
, HashTable
은equals()
메소드로 다시 비교한다.class Client(val name: String, val postalCode: Int) {
...
override fun hashCode(): Int = name.hashCode() * 31 + postalCode
}
데이터를 저장하는 클래스로 toString
, equals
, hashCode
를 생성할 필요도 없이 컴파일러가 자동으로 만들어준다.
equals
와 hashCode
는 주 생성자에 나열된 모든 프로퍼티를 고려해 만들어진다.
(주 생성자 밖에 정의된 프로퍼티는 고려 대상이 x)
data class Client(val name: String, val postalCode: Int)
var
를 써도되지만 모든 프로퍼티를 읽기 전용으로 만들어서 데이터 클래스를 불변 클래스로 만들라고 권장한다.Why?
HashMap 등의 컨테이너에 데이터 클래스 객체를 담는 경우 불변성이 필수적이다. Key로 쓰인 데이터 객체의 프로퍼티를 변경했을 때 컨테이너 상태가 잘못될 수 있기 때문이다. 또한 다중 스레드 프로그램의 경우 불변 객체를 사용하면 스레드가 사용 중인 데이터를 다른 스레드가 변경할 수 없으므로 스레드를 동기화해야 할 필요가 줄어든다.
>>> val client = Client("홍길동", 1111)
>>> println(client.copy(postalCode = 2222).postalCode)
2222
인터페이스를 구현할 때 by 키워드를 통해 그 인터페이스에 대한 구현을 다른 객체에 위임 중이라는 사실을 명시할 수 있다. 다시 말하면, 클래스를 다른 클래스에 위임하면 위임 클래스가 가지는 인터페이스 메소드를 참조 없이 호출하게 해준다.
메소드 중 일부의 동작을 변경하고 싶은 경우 메소드를 오버라이드하면 컴파일러가 생성한 메소드 대신 오버라이드한 메소드가 사용된다.
class DelegatingCollection<T> : Collection<T> {
private val innerList = arrayListOf<T>()
override val size: Int get() = innerList.size
override fun contains(element: T): Boolean = innerList.contains(element)
override fun containsAll(elements: Collection<T>): Boolean
= innerList.containsAll(elements)
override fun isEmpty(): Boolean = innerList.isEmpty()
override fun iterator(): Iterator<T> = innerList.iterator()
}
// by 키워드를 사용한다면 아래와 같이 쓸 수 있다.
class DelegatingCollection<T> (
innerList: Collection<T>
) : Collection<T> by innerList {
// 컴파일러가 생성한 메소드 대신 오버라이드한 메소드가 사용된다.
override fun isEmpty(): Boolean {
println("Empty!!!!")
innerList.isEmpty()
}
}