[Effective Kotlin] 37 - 데이터 집합 표현에 data 한정자를 사용하라
data 한정자를 붙이면, 다음과 같은 몇 가지 함수가 자동으로 생성됩니다.
- toString
- equals와 hashCode 함수
- copy
- componentN(component1, component2 등)
toString 함수는 클래스의 이름과 기본 생성자 형태로 모든 프로퍼티와 값을 출력해줍니다.
(이는 로그를 출력할 때나 디버그할 때 유용하게 사용됩니다.)
equals는 기본 생성자의 프로퍼티가 같은지 확인해 줍니다. 그리고 hashCode는 equals와 같은 결과르 냅니다.
copy는 immutable 데이터 클래스를 만들 때 편리합니다. copy는 기본 생성자 프로퍼티가 같은 새로운 객체를 복사합니다.
새로 만들어진 객체의 값은 이름 있는 아규먼트를 활용해서 변경할 수 있습니다.
val newObj = player.copy( name = "Thor")
print(newObj) // Player(id=0, name=Thor, points=9999)
또한, copy 메서드는 객체를 얕은 복사하지만, 이것은 객체가 immutable이라면 아무런 상관이 없습니다.
(immutable 객체는 깊은 복사한 객체가 필요 없기 때문입니다.)
componentN함수는 위치를 기반으로 객체를 해제할 수 있게 해줍니다.
val (id, name, pts) = player
위의 코드는 코틀린 내부적으로 아래와 같은 코드로 변환합니다.
// 컴파일 후
val id: Int = player.component1()
val name: String = player.component2()
val pts: Int = player.component3()
이렇게 위치를 기반으로 객체를 해제하는 것은 장점도 있고, 단점도 있습니다. 가장 큰 장점은 변수의 이름을 원하는대로 지정할 수 있다는 것입니다. 또한 ComponentN 함수만 있따면, List, Map.Entry 등의 원하는 형태로도 객체를 해제할 수 있습니다.
하지만, 위치를 잘못 지정하면, 다양한 문제가 발생할 수 있어서 위험합니다. 따라서 객체를 해제할 때는 데이터 클래스의 기본 생성자에 붙어 있는 프로퍼티 이름과 같은 이름을 사용하는 것이 좋습니다. 그렇게 하면 순서등을 잘못 지정했을 때 IDE(인텔리제이, 안드로이드 스튜디어)가 관련된 경고를 줍니다.\
튜플 대신 데이터 클래스 사용하기
데이터 클래스는 튜플보다 많은 것을 제공합니다. 구체적으로 코틀린 튜플은 Serializable을 기반으로 만들어지며, toString을 사용할 수 있는 데이터 클래스입니다.
(참고로, 코틀린 튜플에는 Pair와 Triple만 존재합니다.)
튜플은 데이터 클래스와 같은 역활을 하지만, 훨씬 가독성이 나빴습니다. 따라서 점차 없어지게 되었습니다.
따라서 아래의 몇 가지 경우를 제외하고는 데이터 클래스를 사용하는 것이 좋습니다.
- 값에 간단하게 이름을 붙일 때
val (description, color) = when {
degrees < 5 -> "cold" to Color.BLUE
degrees < 23 -> "mild" to Color.YELLOW
else -> "hot" to Color.RED
}
- 표준 라이브러리에서 볼 수 있는 것처럼 미리 알 수 없는 arggregate(집합)를 표현할 때
val (odd, even) = numbers.partition {it % 2 == 1}
val map = mapOf(1 to "San Francisco", 2 to "Amsterdam")
튜플 대신 데이터 클래스를 사용하게 되면 아래와 같은 장점이 있습니다.
- 함수의 리턴 타입이 더 명확해집니다.
- 리턴 타임이 더 짧아지며, 전달하기 쉬워집니다.
- 사용자가 데이터 클래스에 적혀 있는 것과 다른 이름을 활용해 변수를 해제하면, 경고가 출력됩니다.