마흔 번째, 구조 분해 선언입니다.
때때로 객체를 구조 분해하여 얼마간의 변수에 할당하는 것은 편리합니다. 예를 들면 다음과 같습니다.
val (name, age) = person
이러한 구문을 구조 분해 선언(destructuring declaration)이라고 합니다. 구조 분해 선언은 한 번에 복수의 변수를 만듭니다. 위와 같이 하면, name, age 두 개의 변수가 선언되고, 각각을 독립적으로 사용할 수 있습니다.
println(name)
println(age)
구조 분해 선언은 다음과 같은 코드로 컴파일 됩니다.
val name = person.component1()
val age = person.component2()
component1()과 component2() 함수는 Kotlin에서 폭 넓게 사용되는 관례의 원칙(principle of conventions)의 또 다른 예입니다. 필요한 수의 호출할 수 있는 componentN() 함수를 가지 어느 것이든 구조 분해 선언의 오른쪽에 올 수 있습니다.
※ componentN() 함수를 구조 분해 선언에 사용하기 위해서는 operator 키워드를 붙여야 합니다.
구조 분해 선언은 또한 for 루프에서도 동작합니다.
for ((a, b) in collection) { ... }
변수 a와 b는 컬렉션 요소의 componet1()과 component2() 함수 호출 반환값을 각각 갖습니다.
예: 함수에서 두 개의 값 반환
함수에서 두 개의 값을 반환해야 할 필요가 있다고 해 보겠습니다. 두 개 중 하나는 결과 객체이고 하는 어떤 상태 값입니다. Kotlin에서 이를 하기 위한 간단한 방법은 데이터 클래스를 선언하고 이 인스턴스를 반환하는 것입니다.
data class Result(val result: Int, val status: Status)
fun function(...): Result {
// 연산
return Result(result, status)
}
// 함수 사용
val (result, status) = function(...)
데이터 클래스는 자동으로 componentN() 함수가 선언되기 때문에 구조 분해 선언이 동작합니다.
※ 표준 클래스 Pair를 사용할 수도 있습니다. function()이 Pair<Int, Status> 반환하게 하면 됩니다. 하지만, 종종 적절한 프로퍼티 이름을 가진 데이터 클래스를 사용하는 것이 더 나을 때가 있습니다.
예: 구조 분해 선언과 맵
아마 맵을 열거하는 가장 좋은 방법은 다음과 같을 것입니다.
for ((key, value) in map) {
// key와 value를 가지고 뭔가 작업
}
이것이 가능하려면 맵에 다음과 내용이 요구됩니다.
- 값을 연속체로 제공하는 iterator() 함수를 제공할 것
- 각각의 요소는 키와 값 쌍을 위해 component1()과 compoent2() 함수를 가질 것
그리고, 표준 라이브러리는 맵에 대해 이를 충족하는 확장을 제공합니다.
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()
그러므로, 맵(이나 데이터 클래스의 인스턴스를 갖는 컬렉션 내지는 그와 유사한 것)에 대해서 위와 같은 구조 분해 선언을 자유롭게 사용할 수 있습니다.
사용하지 않는 변수를 위한 밑줄(unerscore)
구조 분해 선언에서 필요 없는 변수가 있다면 변수 이름 대신에 밑줄(_)을 사용할 수 있습니다.
val (_, status) = getResult()
이렇게 하면 해당 componentN() 함수는 호출되지 않고 건너 뛰게 됩니다.
람다에서 구조 분해
람다 매개변수를 위해서 구조 분해 선언을 사용할 수 있습니다. 람다의 매개 변수가 Pair 타입(또는 Map.Entry나 적절한 componentN() 함수를 가진 타입)인 경우에는 해당 매개 변수 하나 대신에 괄호를 사용하여 복수의 매개변수를 추가할 수 있습니다.
map.mapValues { entry -> "${entry.value}!" }
map.mapValues { (key, value) -> "$value!" }
두 개의 매개변수와 매개 변수 대신 사용되는 구조 분해 쌍간에 차이점을 주목해야 합니다.
{ a -> ... } // 매개변수 하나
{ a, b -> ... } // 매개변수 둘
{ (a, b) -> ... } // 구조 분해 쌍(pair). 매개 변수로는 하나입니다.
{ (a, b), c -> ... } // 구조 분해 쌍과 다른 하나의 매개변수. 매개 변수로는 두 개.
구조 분해 매개 변수의 요소 중 사용하지 않는 것은 밑줄로 대체할 수 있습니다.
map.mapValues { (_, value) -> "$value!" }
구조 분해되는 매개 변수 전체가 특정한 것에 대해 타입을 지정할 수도 있습니다.
map.mapValues { (_, value): Map.Entry<Int, String> -> "$value!" }
map.mapValues { (_, value: String) -> "$value!" }
'Kotlin' 카테고리의 다른 글
공식 문서로 배우는 코틀린 - 42. Scope functions (0) | 2024.03.18 |
---|---|
공식 문서로 배우는 코틀린 - 41. Reflection (0) | 2024.03.17 |
공식 문서로 배우는 코틀린 - 39. Annotations (0) | 2024.03.16 |
공식 문서로 배우는 코틀린 - 38. This expressions (0) | 2024.03.16 |
공식 문서로 배우는 코틀린 - 37. Equality (0) | 2024.03.16 |