728x90
반응형
✅ 구조 분해 선언과 component 함수
- 코틀린에서 사용하는 관례 중 하나로 구조 분해 선언이라는 것이 있다.
- 구조 분해를 사용하면 복합적인 값을 분해하여 여러 다른 변수를 한꺼번에 초기화할 수 있다.
- 아래 예제를 통해 구조 분해를 사용하는 방법을 알 수 있다.
val p = Point(10, 20)
val (x, y) = p // x와 y 변수를 선언한 다음에 p의 여러 컴포넌트로 초기화한다.
println(x)
// 10
println(y)
// 20
- 구조 분해 선언은 일반 변수 언언과 비슷해 보이지만 =의 좌변에 여러 변수를 괄호로 묶는 다는 점에서 다르다.
- 구조 분해 선언의 내부에서는 다시 관례를 사용한다.
- 구조 분해 선언의 각 변수를 초기화하기 위해 componentN이라는 함수를 호출한다.
- 이때, N은 구조 분해 선언에 있는 변수 위치에 따라 붙는 번호이다.
- 앞에서 살펴본 val (x, y) = p는 아래 그림과 같이 컴파일 된다.
- data 클래스의 주 생성자에 들어있는 프로퍼티에 대해서는 컴파일러가 자동으로 componentN 함수를 만들어준다.
- 아래 예제는 데이터 타입이 아닌 클래스의 경우 이런 함수를 어떻게 구현하는지 보여준다.
class Point(val x: Int, val y: Int) {
operator fun component1() = x
operator fun component2() = y
}
- 이러한 구조 분해 선언은 함수에서 여러 값을 반환할 때 유용하다.
- 여러 값을 한꺼번에 반환해야 하는 함수가 있는 경우 반환해야 하는 모든 값이 들어갈 데이터 클래스를 정의하고 함수의 반환 타입을 그 데이터 클래스로 바꾼다.
- 구조 분해 선언 구문을 사용하면 이런 함수가 반환하는 값을 쉽게 풀어서 여러 변수에 넣을 수 있다.
- 또한 경우에 따라 값이 필요하지 않은 경우 밑줄(_)로 대체가 가능하다.
class Name(val name1: String, val name2: String, val name3: String) {
operator fun component1() = name1
operator fun component2() = name2
operator fun component3() = name3
}
val name = Name("다훈1", "다훈2", "다훈3")
val (name1, _, name3) = name
println("name1 : ${name1}")
// name1 : 다훈1
println("name3 : ${name3}")
// name3 : 다훈3
- 이러한 구조 분해 선언은 맨 앞의 다섯 원소에 대한 componentN을 제공한다.
- 컬렉션 크기를 벗어나는 위치의 원소에 대한 구조 분해 선언을 사용하면 실행 시점에 java.lang.ArrayIndexOutOfBoundsException 등의 예외가 발생한다.
- 또한 여섯 개 이상의 변수를 사용하는 구조 분해를 컬렉션에 사용할 경우 component6 등에 의한 컴파일 오류가 발생한다.
- 또한, 표준 라이브러리의 Pair나 Triple 클래스를 사용하면 함수에서 여러 값을 더 간단하게 반환할 수 있다.
❗ Pair와 Triple 클래스
- Pair와 Triple은 그 안에 담겨있는 원소의 의미를 말해주지 않으므로 경우에 따라 가독성이 떨어질 수 있는 반면, 직접 클래스를 작성할 필요가 없어 코드는 더 단순해진다.
/* Pair */
val pair_test1 = Pair("dahoon", 613)
val pair_test2 = Pair<String, Int>("dahoon", 613)
val (a, b) = Pair("dahoon", 613)
// Pair 객체를 선언 시 내부 객체들의 타입이 달라도 된다.
// 제네릭 타입으로 사용이 가능하다.
/* Triple */
// Pair와 유사하다.
val triple = Triple<String, Int, Int>("dahoon", 180, 78)
val (a, b, c) = Triple("dahoon", 180, 78)
📌 구조 분해 선언과 루프
- 함수 본문 내의 선언문뿐 아니라 변수 선언이 들어갈 수 있는 장소라면 어디든 구조 분해 선언을 사용할 수 있다.
- 예를 들어 루프 안에서도 구조 분해 선언을 사용할 수 있다.
- 특히 맵의 원소에 대해 이터레이션할 때 구조 분해 선언이 유요하다.
- 아례 에제를 통해 확인해보자.
/* 구조 분해 선언을 사용해 맵 이터레이션하기 */
fun printEntries(map: Map<String, String>) {
for((key, value) in map) { // 루프 변수에 구조 분해 선언을 사용한다.
println("$key -> $value")
}
}
val map = mapOf("Oracle" to "Java", "JetBrains" to "Kotlin")
printEntries(map)
// Oracle -> Java
// JetBrains -> Kotlin
- 위 예제에서는 두 가지의 코틀린 관례를 활용하는 것을 볼 수 있다.
- 하나는 객체를 이터레이션하는 관례이고, 다른 하나는 구조 분해 선언이다.
- 코틀린 표준 라이브러리에는 맵에 대한 확장 함수로 iterator가 들어 있다.
- 그 iterator는 맵 원소에 대한 이터레이터를 반환한다.
- 따라서 자바와 달리 코틀린에서는 맵을 직접 이터레이션할 수 있다.
- 또한 코틀린 라이브러리는 Map.Entry에 대한 확장 함수로 component1과 component2를 제공한다.
- 위의 루프는 이런 확장 함수를 사용하는 아래 코드와 같다.
for (entry in map.entries) {
val key = entry.component1()
val value = entry.component2()
// ...
}
728x90
반응형
'Studying > Kotlin' 카테고리의 다른 글
[Kotlin In Action] 7장. 연산자 오버로디오가 기타 관례(5) - 프로퍼티 접근자 로직 재활용 : 위임 프로퍼티 (0) | 2023.06.07 |
---|---|
[Kotlin] 위임 패턴(Delegation Pattern)과 위임(Delegation) 그리고 상속(Inheritance) (2) | 2023.06.05 |
[Kotlin In Action] 7장. 연산자 오버로딩과 기타 관례(3) - 컬렉션과 범위에 대해 쓸 수 있는 관례 (0) | 2023.06.02 |
[Kotlin In Action] 7장. 연산자 오버로딩과 기타 관례(2) - 비교 연산자 오버로딩 (0) | 2023.06.02 |
[Kotlin In Action] 7장. 연산자 오버로딩과 기타 관례(1) - 산술 연산자 오버로딩 (0) | 2023.06.02 |