24-05-05 Kotlin 구조 분해 선언
오늘은 스프링 입문 강의도 보고 알고리즘 공부도 하는 시간을 가졌다.
다른 사람들이 작성한 코드들을 보고 구조 분해 선언이라는 것을 알게되었고, 따라서 몇 번 사용해본 적이 있었는데 이것에 대해 제대로 알고있지는 못했어서 오늘은 이 부분에 대해 정리해보았다.
1. 구조 분해 선언
1-1. 구조 분해 선언이란?
객체가 가지고 있는 여러 값을 한 번에 여러 개의 변수에 넣어 선언하는 것을 말한다. 이렇게 생성된 변수들은 모두 독립적으로 사용할 수 있다.
코드로 직접 작성해 확인해보면 직관적이고 이해하기 쉽다. 아래는 Pair를 구조 분해 선언하여 두 개의 변수에 값을 한 번에 담는 것을 보여준다.
fun main() {
val pair = "하하" to 20
val (name, age) = pair
println("name: $name, age: $age") // name: 하하, age: 20
}
위의 코드에서 한 줄로 적혀있는 구조 분해 선언은 실제로 뜯어보면 아래와 같은 두 줄의 코드와 같다.
val (name, age) = pair
// 위의 한 줄과 아래 두 줄은 동일 하다
val name = pair.component1()
val age = pair.component2()
데이터 클래스의 경우 componentN()이라는 함수가 자동으로 만들어진다.(N은 1부터 시작하는 자연수들이다.) Pair는 코틀린에서 제공하는 표준형 데이터 클래스 중 하나이고 component1(), component2() 함수가 자동으로 만들어지기 때문에 구조 분해 선언을 할 수 있는 것이다.
구조 분해 선언에서는 대입연산자의 오른쪽에 위치하는 대입되는 값에 해당하는 부분에 필요한 수의 componentN() 함수를 불러올 수만 있다면 어떤 것이든 대입할 수 있다.
코드를 작성해 확인해보는 것이 이해가 더 빠르다. 아래처럼 임의의 클래스를 하나 만들고 연산자 오버로딩을 이용해 componentN() 함수를 오버로드하여 정의해주면 구조 분해 선언을 할 수 있다.
fun main() {
val test = TestClass(10,20,30)
val (x,y,z) = test
println("x: $x, y: $y, z: $z")
// x: 10, y: 20, z: 30
val q = test.component1()
val w = test.component2()
val e = test.component3()
println("q: $q, w: $w, e: $e")
// q: 10, w: 20, e: 30
}
class TestClass (val a:Int, val b:Int, val c:Int) {
operator fun component1():Int = a
operator fun component2():Int = b
operator fun component3():Int = c
}
위에서 언급했듯 데이터 클래스의 경우는 주생성자에 들어있는 속성의 경우 자동으로 순서에 따라 componentN() 함수가 만들어진다. 배열이나 컬렉션에서도 앞의 5개에 해당하는 요소에 대해서는 componentN() 함수가 자동으로 만들어진다.
1-2. 배열과 컬렉션에서의 구조 분해 선언
배열과 컬렉션에서는 앞에서부터 5개의 해당하는 요소에 대해서는 componentN() 함수가 자동으로 만들어지기 때문에 그냥 구조 분해 선언을 이용할 수 있다.
만약 5개를 넘어서는 숫자의 요소에 대해 구조 분해 선언을 이용하고 싶다면 확장함수와 연산자 오버로딩을 이용해 componentN() 함수를 정의해주어야 한다. 아래 코드에서는 List에 확장함수를 이용해 component6()를 정의해보고 사용해보았다.
fun main() {
val list = listOf(1,2,3,4,5,6)
// 앞에서부터 5개의 요소에 대해서는 그냥 구조 분해 선언이 가능하다
val (x,y,z) = list
println("x: $x, y: $y, z: $z")
// x: 1, y: 2, z: 3
// 6개부터는 연산자 오버로딩으로 직접 componentN() 함수를 정의해야한다
val (a,b,c,d,e,f) = list
println("a: $a, b: $b, c: $c, d: $d, e: $e, f: $f")
// a: 1, b: 2, c: 3, d: 4, e: 5, f: 6
}
operator fun <E> List<E>.component6() = this[5]
1-3. 데이터 클래스, Pair와 Triple
위에서도 언급했다시피 데이터 클래스는 주생성자에 들어있는 속성에 대해서는 componentN() 함수가 자동으로 만들어지기 때문에 그냥 구조 분해 선언을 이용할 수 있다.
Pair와 Triple은 두 개, 세 개의 값을 저장할 수 있는 데이터 클래스로 하나의 함수에서 2개 또는 3개의 값을 반환받는 등 필요한 경우에 편하게 사용할 수 있다.
아래 코드에서처럼 데이터 클래스를 이용하면 쉽게 구조 분해 선언을 이용해 객체 안의 값들을 여러 변수에 한 번에 넣어줄 수 있다.
fun main() {
val pair = "메롱" to 30.0
val triple = Triple("하하", 2, 'A')
val (a, b) = pair
val (x, y, z) = triple
println("a: $a, b: $b, x: $x, y: $y, z: $z")
// a: 메롱, b: 30.0, x: 하하, y: 2, z: A
val numbers = Numbers(11, 22, 33, 44, 55, 66)
val (q, w, e, r, d, f) = numbers
println("q: $q, w: $w, e: $e, r: $r, d: $d, f: $f")
// q: 11, w: 22, e: 33, r: 44, d: 55, f: 66
}
data class Numbers (
val num1:Int,
val num2:Int,
val num3:Int,
val num4:Int,
val num5:Int,
val num6:Int
)
이외에도 루프문 안에서 구조 분해 선언을 활용하는 법, 람다안에서 구조 분해하는 법 등 여러가지 더 정리해보고 싶은 것들이 있지만 정리하는데 생각보다 시간이 오래 걸려서 오늘은 여기까지 정리해보겠다.
내일마저 정리해서 올릴 예정이다.
2. 오늘 배운 것
- 코틀린의 구조 분해 선언에 대해 자세히 알아보고 정리해보았다.
- 스프링 입문 강의를 모두 들었다. 웹 어플리케이션 만들기를 빨리 해보고 싶다.