Destructuring : https://kotlinlang.org/docs/destructuring-declarations.html
코틀린은 클래스의 속성값을 하나씩 받아오지 않고, 한번에 여러 변수에 받아오는 방법을 제공한다. 이를 destructuring이라고 한다. 예를들면 다음과 같이 사용한다.
val (name, age) = person
val name = person.component1()
val age = person.component2()
위 코드는 person에 있는 값을 name과 age 변수에 받아오고 있다. 이게 가능한 이유는 위에 풀어쓴 내용처럼 component1(), component2()로 해석되기 때문이다.
component1, component2 등의 함수는 계속 3, 4 등으로 이어질 수 있는데, 이를 componentN()이라고 하며, 이는 operator로 구현해야 한다. 예를들면 Map에는 다음과 같이 정의되어 있다.
operator fun <K, V> Map<K, V>.iterator(): Iterator<Map.Entry<K, V>> = entrySet().iterator()
operator fun <K, V> Map.Entry<K, V>.component1() = getKey()
operator fun <K, V> Map.Entry<K, V>.component2() = getValue()
만약, 사용하지 않는 값을 받는경우 다음과 같이 변수 대신에 언더스코어 ‘_’를 이용할 수 있다.
val (_, status) = getResult()
람다 표현식에서도 destructuring을 이용할 수 있다.
map.mapValues { entry -> "${entry.value}!" }
map.mapValues { (key, value) -> "$value!" }

data class : https://kotlinlang.org/docs/data-classes.html
프로그래밍 언어에서 구조적인 데이터를 저장하기 위해 C 라면 구조체를 이용하고, OOP 언어라면 클래스를 새로 만들게 된다. Kotlin에서는 data class라는 것으로 코드도 단순하게 만들고 사용도 쉽게 지원해주고 있다. 클래스에 data 키워드를 사용한다. 가장 간단한 형태는 다음과 같다.
data class User(val name: String, val age: Int)
클래스 바디도 필요없고, property들만 나열되어 있는 것으로 매우 단순하게 표현이 가능하다. 이렇게 사용할 수 있는 이유는 data class에서 기본적으로 데이터를 다루는데 필요한 equals()/ hashCode(), toString(), componentN(), copy() 를 구현해주고 있기 때문이다. 물론, 클래스 바디도 필요하다면 구현 가능하다.
data class 는 매우 유용한데, Sealed class와 짝을이뤄 자주 사용된다.

enum 과 sealed class :
https://kotlinlang.org/docs/enum-classes.html
https://kotlinlang.org/docs/sealed-classes.html
이에 대해선 예전에 한 번 정리했던 적이 있어 자세한 설명은 생략한다 : http://batmask.dothome.co.kr/index.php/2021/06/29/869/
enum은 다른 언어와 유사하게 상수를 나열하여 사용할 때 유용하다.
enum class Direction {
NORTH, SOUTH, WEST, EAST
}
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
valueOf(), entries, name, ordinal을 이용해 다음과 같이 사용 가능하다.
enum class RGB { RED, GREEN, BLUE }
fun main() {
for (color in RGB.entries) println(color.toString()) // prints RED, GREEN, BLUE
println("The first color is: ${RGB.valueOf("RED")}") // prints "The first color is: RED"
}
...
println(RGB.RED.name) // prints RED
println(RGB.RED.ordinal) // prints 0
코틀린에서는 enum과 유사한 역할을 하는데 sealed class를 자주 이용한다. sealed class나 인터페이스는 원래, 상속을 모듈과 패키지에 제한 하는 키워드로, 컴파일 타임에만 인식하여 상속이 가능하다. 그러니까 별도로 컴파일된 모듈간 상속이 제한된다. 라이브러리의 남용을 막기 위해 사용가능한 방법이기도 하다.
Sealed class를 enum대신에 유용하게 사용하는 예가 아예 공식문서화 되어 있다. ( https://kotlinlang.org/docs/sealed-classes.html#use-case-scenarios )
sealed class UIState {
data object Loading : UIState()
data class Success(val data: String) : UIState()
data class Error(val exception: Exception) : UIState()
}
fun updateUI(state: UIState) {
when (state) {
is UIState.Loading -> showLoadingIndicator()
is UIState.Success -> showData(state.data)
is UIState.Error -> showError(state.exception)
}
}
예전 방식의 프로그래밍이라면, 아마도 enum으로 처리했을 상태들이다. sealed class UIState를 상속받아 data object 와 class들을 만들었다. 이를 UI state를 체크하는 when문에서 사용하고 있다. data object로 만들면 하나의 객체만 다루기 때문에 enum과 같이 상수처럼 다룰 수 있으며, data class 로 생성하면 enum과는 다르게 여러개의 인스턴스 생성이 가능하다. 또한, data class에 여러 정보들을 저장하고 사용하는게 enum보다 훨씬 간편하고 유용하다. 또하나의 예가 나와있다.
sealed class Payment {
data class CreditCard(val number: String, val expiryDate: String) : Payment()
data class PayPal(val email: String) : Payment()
data object Cash : Payment()
}
fun processPayment(payment: Payment) {
when (payment) {
is Payment.CreditCard -> processCreditCardPayment(payment.number, payment.expiryDate)
is Payment.PayPal -> processPayPalPayment(payment.email)
is Payment.Cash -> processCashPayment()
}
}
각각의 payment 방법에 따라 다른 정보들을 들고 있고, 이를 매우 편리하게 처리하고 있다. 같은 슈퍼클래스인 payment를 가지고 CreditCard, PayPal, Cash 각각을 캐스팅 없이 처리하고 있는데, 이는 코틀린의 스마트 캐스트( https://kotlinlang.org/docs/typecasts.html#smart-casts ) 기능 덕분이다. 스마트 캐스트는 타입을 체크하는 경우, 알아서 캐스팅을 해주게 된다.