본문 바로가기
2023년 이전/kotlin

kotlin - 컬렉션 처리(가변 길이 인자, 중위 함수 호출, 라이브러리 지원)

by JeongUPark 2020. 2. 20.
반응형

[출처 -  Kotlin In Action] [아래 내용들은 Kotlin In Action을 공부하면서 스스로 정리한 내용입니다] 


이번에 살펴볼 내용은 다음과 같습니다.

 

  • vararg 키워드를 사용하면 호출 시 인자 개수가 달라질 수 있는 함수를 정의할 수 있다.
  • 중위(infix) 함수 호출 구문을 사용하면 인자가 하나뿐인 메소드를 간편하게 호출 할 수 있다.
  • 구조 분해 선언을 사용하면 복합적인 값을 분해해서 여러 변수에 나눠 담을 수 있다.

자바 컬렉션 API 확장

kotlin은 자바와 같은 컬렉션 클래스를 사용하지만 더 확장된 API를 제공합니다. 예를 들어

fun main(args: Array<String>) {
    val strings:List<String> = listOf("First","Second","Third")
    println(strings.last())
    val numbers : List<Int> = listOf(1,14,2)
    println(numbers.max())
}

이렇게 해서 리스트의 마지막 원소나, 가장 큰 값을 획득할 수 있습니다.(결과는 Third와 14 입니다.)

이것이 가능한 이유는 위의 Last와 max는 확장함수이기 때문입니다. 두 함수의 원본을 확인해 보겠습니다.

public fun <T> List<T>.last(): T {
    if (isEmpty())
        throw NoSuchElementException("List is empty.")
    return this[lastIndex]
}
public fun <T : Comparable<T>> Iterable<T>.max(): T? {
    val iterator = iterator()
    if (!iterator.hasNext()) return null
    var max = iterator.next()
    while (iterator.hasNext()) {
        val e = iterator.next()
        if (max < e) max = e
    }
    return max
}

이렇게 last와 max가 확장함수로 _Collctions.kt에 정의 되어 있습니다. kotlin의 이런 표준 라이브러리 확장 함수는 last나 max 뿐만 아니라, 훨씬 많이 존재하지만 이런 것들을 모두 알 필요는 없고, 개발툴에서 코드완성 기능을 통해 확인하고 필요한 기능을 사용하시면 됩니다.

 

가변 인자 함수 : 인자의 개수가 달라질 수 있는 함수 정의

리스트를 생성하는 listof 함수의 정의를 보면 다음과 같습니다.

public fun <T> listOf(vararg elements: T): List<T>

vararg인자는 가변 길이 인자로 , 메소드를 호출할 때 원하는 개수만큼 값을 인자로 넘기면 자바 컴파일러가 배열에 그 값들을 넣어주는 기능을 합니다. 사용 방법은 파라미터 앞에 vararg를 붙입니다.

 

배열에 들어있는 원소를 가변 길이 인자로 넘길떄 자바는 그냥 넘지만, 코들린의 경우에는 명시적으로 풀어서 배열의 각 원소가 인자로 전되게 해야하는데 이 부분은 스프레드 연산자가 대신해줍니다. 스프레드 연산자가 대신해준다고 했지만 개발자는 배열 앞에 *를 붙여 주기만 하면 됩니다.

 

 

값의 쌍 다루기 : 중위 호출과 구조 분해 선언

다음 코드를 보면

val map = mapOf(1 to "one", 2 to "two", 3 to "three")

to 라는 단어가 있습니다. 이 to 라는 단어는 코틀린 키워드가 아니고 중위 호출이라는 방식으로 to 라는 일반 메소드를 호출 한 것입니다.

1.to("one")
1 to "one"

위의 두 코드는 같은 방식으로 첫번째는 to 메소드를 일반 방식으로 호출한 것이고 두번째는 to 메소드를 중위 호출 방식으로 호출한 것입니다.

 

즉, 중위호출은 인자가 하나뿐인 일반 메소드나 인자가 하나뿐인 확장함수에서 사용할 수 있습니다. 그리고 함수를 중위호출에 사용하게 허용하려면 함수 선언 앞에 infix라는 변경자를 추가해야 합니다.

 

infix fun Any.to(other:Any) = Pair(this,other)

위의 to 함수는 Pair 인스턴스를 반환합니다. (Pair는 코들린 표준라이브러리로 여기서 확인 하시면 됩니다. 그리고 to는 제너릭 함수지만 설명을 위해 세부 사항은 생략했습니다.)

 위의 to를 사용해보면

fun main(args: Array<String>) {
    val (number, name) = 1 to "one"
    println("number: $number")
    println("name: $name")
}
infix fun Any.to(other:Any) = Pair(this,other)

결과는

number: 1
name: one

그리고 위의 val (number, name) = 1 to "one"을 구조 분해 선언이라 부릅니다. 왜 그렇게 되는지 위의 code를 통해 설명 드리면 1 과 "one"이 to를 통해 Pair가 되고 이 Pair의 값들이  val (number,name)의 각각에 들어가게 됩니다. 

Pair 인스턴스 외 다른 객체에도 구조 분해를 적용할 수 있습니다. 예를 들어 key와 value라는 두 변수를 map의 원소로 사용해 초기화 할 수 있습니다.

또한 withIndex를 사용하여 루프에서 구조분해선언을 활용할 수 있습니다.

fun main(){
    val strings = listOf("first", "second", "fourteenth")
    for((index,element) in strings.withIndex()){
        println("$index: $element")
    }
}
0: first
1: second
2: fourteenth

정리하면 to 함수는 확장 함수이고, to를 사용하면 타입과 관련 없이 임의의 순서쌍을 만들수 있습니다. 이는 to의 수신 객체가 제네릭하다는 뜻입니다.

반응형