본문 바로가기

코틀린

코틀린(Kotlin) - 구조 분해 선언과 component 함수

데이터 클래스의 특성 중 convention 원리와 관련된 특성인 구조 분해 선언(destructuring declaration)에 대해 살펴보도록 하겠습니다.

 

데이터 클래스에 대한 기본적인 내용은 아래 포스팅을 참고하시면 됩니다. ^^

2020.03.03 - [코틀린] - 코틀린(Kotlin) - 데이터 클래스와 클래스 위임(by)

 

코틀린(Kotlin) - 데이터 클래스와 클래스 위임(by)

자바와 마찬가지로 코틀린 클래스도 toString, equals, hashCode 등을 오버라이드 할 수 있고, 코틀린은 이런 메소드 구현을 자동으로 생성해줄 수 있습니다. 데이터 클래스 어떤 클래스에 대해 toString,

0391kjy.tistory.com

디스트럭쳐링(Destructuring)

구조 분해를 사용하면 객체가 가지고 있는 여러 값을 분해해서 여러 변수에 한꺼번에 초기화할 수 있습니다.

 

/* 구조 분해 간단한 예제 */
>>> val car = Car("현대", "그랜저 IG")
>>> val (manufacturer, model) = car  //변수를 선언함과 동시에 car의 여러 컴포넌트로 초기화 합니다.
>>> println(manufacturer)
현대
>>> println(model)
그랜저 IG

 

구조 분해 선언은 일반 변수 선언과 비슷해 보이지만 "=" 의 좌변에 여러 변수를 괄호로 묶었다는 점에서 차이가 있습니다.

구조 분해 선언의 내부에서는 각 변수를 초기화하기 위해 componentN이라는 함수를 호출하게 되며, 여기서 N은 구조 분해 선언에 있는 변수 위치에 따라붙는 번호입니다.

즉, 앞에서 살펴본 val (manufacturer, model) = car 는 아래와 같이 컴파일됩니다.

 

구조 분해 선언은 componentN 함수 호출로 변환됩니다.

 

데이터 클래스의 주 생성자에 들어있는 프로퍼티에 대해서는 컴파일러가 자동으로 componentN 함수를 만들어줍니다. 👍

 

다음 예제는 데이터 클래스가 아닌 일반 클래스에서 구현한 예제입니다.

 

/* 일반 클래스에서 구조 분해 선언하는 예제 */
class Car(val manufacturer: String, val model: String) {
   operator fun component1() = manufacturer
   operator fun component2() = model
}

 

이러한 구조 분해 선언은 함수의 리턴 값이 여러 개일 때 유용합니다.

반환할 모든 값이 들어갈 데이터 클래스를 정의하고 함수의 반환 타입을 이 데이터 클래스로 하면 됩니다!

구조 분해 선언을 통해 이런 함수가 반환하는 값을 쉽게 풀어서 여러 변수에 할당할 수 있습니다.

 

경우에 따라서 모든 값이 필요하지 않다면 사용되지 않는 값은 밑줄(_) 로 대체할 수도 있습니다.

 

val car = Car("현대", "그랜저", "그랜저 IG")
val (manufacturer, _, model) = car

>>> println("제조사 : ${manufacturer}, 모델 : ${model}")

 

이러한 구조 분해 선언은 무한하게 componentN을 선언할 수는 없고 맨 앞의 다섯 개 원소에 대해서만 componentN을 제공합니다.

 

이와 비슷한 기능으로는 코틀린 표준 라이브러리에 미리 정의된 PairTriple 클래스를 사용하면 함수에서 여러 값을 더 간단하게 반환할 수 있습니다.

Pair 나 Triple은 그 안에 담긴 원소의 의미를 겉으로는 알 수 없기 때문에 경우에 따라 가독성이 떨어질 수 있는 반면에, 직접 데이터 클래스를 작성할 필요가 없으므로 코드는 훨씬 더 단순해지고 간결해집니다.

 

/* Pair 예제 */
val pair = Pair("현대", "그랜저 IG")

>>> println(pair.first)
>>> println(pair.second)

또는

>>> println(pair.component1)
>>> println(pair.component2)

또는

val (manufacturer, model) = Pair("현대", "그랜저 IG")
>>> println(manufacturer)
>>> println(model)

 

/* Triple 예제 */
val triple = Triple("현대", "그랜저", "그랜저 IG")

>>> println(triple.first)
>>> println(triple.second)
>>> println(triple.third)

또는

>>> println(triple.component1)
>>> println(triple.component2)
>>> println(triple.component3)

또는

val (manufacturer, modelGroup, model) = Triple("현대", "그랜저", "그랜저 IG")
>>> println(manufacturer)
>>> println(modelGroup)
>>> println(model)

 

위와 같이 코틀린에서는 Pair(두 개의 값 반환), Triple(세 개의 값 반환) 을 제공해주고 있으며, 만약 4개 이상의 값을 반환하고자 한다면 그런 경우는 데이터 클래스를 만들고 그 클래스 객체를 반환하는 방식으로 사용하시는 것을 권장드립니다. ^^ 👍

구조 분해 선언과 루프

구조 분해는 변수 선언이 들어갈 수 있는 곳이라면 어디든 구조 분해 선언을 사용할 수 있습니다.

예를 들어 루프 안에서도 구조 분해 선언을 사용할 수 있습니다.

특히, Map의 원소나 List에 대해 이터레이션 할 때 구조 분해 선언이 굉장히 유용하게 사용됩니다.

 

/* 구조 분해 선언을 사용해 Map 이터레이션하기 */
val map = mapOf("JetBrains" to "Kotlin")

for((key, value) in map) {  //루프 변수에 구조 분해 선언을 사용
   println("${key} -> ${value}")
}

>>> JetBrains -> Kotlin

 

/* 구조 분해 선언을 사용해 List 이터레이션하기 */
val list = listOf("Android", "Kotlin", "Java")

for((index, element) in list.withIndex()) {  //루프 변수에 구조 분해 선언을 사용
   println("${index} -> ${element}")
}

>>> 0 -> Android
>>> 1 -> Kotlin
>>> 2 -> Java