구조 분해 선언(destructing declaration)을 사용하면 복합적인 값을 분해해서 여러 다른 변수를 한꺼번에 초기화할 수 있다.

data class Point(val x: Int, val y: Int)

fun main() {
    val p = Point(10, 20)
    val (x, y) = p
    println(x) // 10
    println(y) // 20
}

내부에서 구조 분해 선언은 다시 관례를 사용한다. 구조 분해 선언의 각 변수를 초기화하기 위해 componentN이라는 함수를 호출한다. 여기서 N은 구조 분해 선언에 있는 변수 위치에 따라 붙는 번호다. 앞에서 살펴본 val (x, y) = p는 다음과 같이 컴파일된다.

val a = p.component1()
val b = p.component2()

data 클래스의 주 생성자에 들어있는 프로퍼티에 대해서는 컴파일러가 자동으로 componentN 함수를 만들어준다. 데이터 타입이 아닌 클래스에 대해서 구조 분해 선언을 사용하려면 componentN 함수를 직접 구현해야 한다.

class Point(val x: Int, val y: Int) {
    operator fun component1() = x
    operator fun component2() = y
}

구조 분해 선언은 함수에서 여러 값을 반환할 때 유용하다. 여러 값을 한꺼번에 반환해야 하는 함수가 있다면 반환해야 하는 모든 값이 들어갈 데이터 클래스를 정의하고 함수의 반환 타입을 그 데이터 클래스로 바꾼다.

data class NameComponents(val name: String, val extension: String)

fun splitFileName(fullName: String): NameComponents {
    val result = fullName.split('.', limit = 2)
    return NameComponents(result[0], result[1])
}

fun main() {
    val (name, ext) = splitFileName("example.kt")
    println(name) // example
    println(ext) // kt
}

배열이나 컬렉션에도 componentN 함수가 있으므로, 다음과 같이 코드를 개선할 수 있다.

data class NameComponents(val name: String, val extension: String)

fun splitFileName(fullName: String): NameComponents {
    val (name, extension) = fullName.split('.', limit = 2)
    return NameComponents(name, extension)
}

코틀린 표준 라이브러리에서는 맨 앞의 다섯 원소에 대한 componentN을 제공한다. 이 특징으로 인해 컬렉션 크기가 5보다 작아도 여전히 component1부터 component5까지 사용 가능하다.

컬렉션 크기를 벗어나는 위치의 원소에 대한 구조 분해 선언을 사용하면 실행 시점에 java.lang.ArrayIndexOutOfBoundsException 예외가 발생한다.

fun main() {
    val x = listOf(1,2)
    val (a,b,c,d,e) = x // Index 2 out of bounds for length 2
}

여섯 개 이상의 변수를 사용하는 구조 분해를 컬렉션에 대해 사용하면 component6 등에 의한 컴파일 오류가 발생한다.

Destructuring declaration initializer of type List<Int> must have a 'component6()' function

fun main() {
    val x = listOf(1,2)
    val (a,b,c,d,e, f) = x // compile error
}