3 minute read

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

  • 컬렉션을 처리할 때 쓸 수 있는 코틀린 표준 라이브러리 함수 몇가지를 알아보자.

자바 컬렉션 API 확장

앞에서 코틀린 컬렉션은 자바와 같은 클래스를 사용하지만 더 확장된 API를 제공한다고 했다.
예를들어 리스트의 마지막 원소를 가져오거나, 컬렉션의 최댓값을 찾는 것들을 확인할 수 있었다.

이런 기능은 이제는 확장 함수를 이용해서 가능하다는 것을 우리는 알 수 있다.
fun <T> List<T>.last(): T {// 마지막 원소를 반환하는 로직}
fun Collection<Int>.max(): Int {// 컬렉션의 최댓값을 찾는 로직}

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

리스트를 생성하는 함수를 호출할 때 원하는 만큼 많이 원소를 전달할 수 있다.
val list = listOf(1, 2, 3, 4, 5, 6)
이 함수의 정의를 보면 다음과 같다.
fun listOf<T>(vararg values: T): List<T> {...}
우선 간단하게 자바의 가변인자에 대해 알아보자.
JDK5부터 가변인자 메서드라고 부르는 지정된 자료형의 인자를 0개 이상 받을 수 있는 varags 메서드가 추가되었다.
public class Test{
    public void test(String ... strings) {
        Arrays.stream(strings).forEach(System.out::println);
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.test("hi", "bye", "test");
    }
}

이렇게 파라미터 변수명 앞에 ...을 붙이면 컴파일러가 파라미터를 배열 형식으로 바꾸고, 매개변수로 주어지는
변수들을 모아서 객체로 만든다.
코틀린의 가변 길이 인자는 타입 뒤에 "..." 를 붙이는 대신 코틀린에서는 파라미터 앞에 vararg 변경자를 붙인다.

이미 배열에 들어있는 원소를 가변 길이 인자로 넘길 때도 코틀린과 자바 구문이 다르다.
자바에서는 배열을 그냥 넘기면 되지만, 전달하려는 배열 앞에 *를 붙이기만 하면 된다.
// java
public class Test {
    public void test(String... strings) {
        Arrays.stream(strings).forEach(System.out::println);
    }

    public static void main(String[] args) {
        String[] strings = new String[2];
        strings[0] = "hi";
        strings[1] = "bye";
        new Test().test(strings); // 가변인자에 배열을 그냥 넘긴다.
    }
}
// kotlin
fun main(args: Array<String>) {
    val array = arrayOf("hi", "hello", "bye")
    test(*array) // 가변인자에 *을 붙여서 넘긴다.
}

fun test(vararg strings: String) {
    println(strings.joinToString(", "))
}

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

fun main(args: Array<String>) {
    val map = mapOf(1 to "one", 2 to "two", 3 to "three")
}
이렇게 mapOf 함수를 사용하여 맵을 만들 수 있다.
맵을 만들 때 사용한 to는 코틀린 키워드가 아닌 일반 메소드이고,
"중위 호출"이라는 방식으로 to 메소드를 호출한 것이다.

중위 호출

중위 호출 시에는 수신 객체와 유일한 메소드 인자 사이에 메소드 이름을 넣는다.
fun main(args: Array<String>) {
    // 동일한 호출
    1.to("one") // to 메소드를 일반적인 방식으로 호출
    1 to "one"  // to 메소드를 중위 호출 방식으로 호출
}
인자가 하나뿐인 메소드나 인자가 하나뿐인 확장 함수에 중위 호출을 사용할 수 있다.
to 함수는 코틀린 표준 라이브러리 클래스인 Pair 인스턴스를 반환하고, Pair 인스턴스는
두 원소로 이뤄진 순서쌍을 표현한다.
아래와 같이 사용할 수 있다.
fun main(args: Array<String>) {
    val (number, name) = 1 to "one"  // to 메소드를 중위 호출 방식으로 호출
    println("number: $number, name: $name") // number: 1, name: one
}
** Pair 인스턴스 외 다른 객체에도 구조 분해를 적용할 수 있다.
예를 들어 key와 value라는 두 변수를 맵의 원소를 사용해 초기화할 수 있다.(p.128)

문자열과 정규식 다루기

코틀린 문자열과 자바 문자열은 같지만 코틀린에서는 다양한 확장 함수를 제공한다.

문자열 나누기

자바에서는 문자열을 나누기 위해서 String의 split 메소드를 사용한다.
split 메소드의 구분 문자열은 정규식이기 때문에 "."를 구분자로 사용할 경우 
"12.345.678".split(".") 의 결과로 빈 배열을 리턴받을 것이다.
"." 는 모든 문자를 나타내는 정규식으로 해석되기 때문이다.
코틀린에서는 여러가지 다른 조합의 파라미터를 받는 split 확장 함수를 제공한다.
정규식을 파라미터로 받는 함수는 Stirng이 아닌 Regex 타입의 값을 받기 때문에
split 함수에 전달하는 값의 타입에 따라 정규식이나 일반 텍스트로 구분해서 문자열을 분리할 수 있다.
println("12.345-6.A".split("\\.|-".toRegex())) // [12,345,6,A] -> 정규식으로 구분
또한 구분 문자열을 하나 이상 인자로 넘길 수 있다.
println("12.345-6.A".split(".", "-")) // [12,345,6,A] -> 여러 구분 문자열 지정

코드 다듬기: 로컬 함수와 확장

코드를 작성하다보면 함수 내부에서만 사용되는 기능을 별도 함수로 분리하는 경우가 있다.
이런 함수 내부 로직이 추출된 함수가 많아지면 각 메소드 사이의 관계를 파악하기 힘들어서 코드를 이해하기 더 어려워질 수 있다.
파이썬에서는 이러한 문제를 해결하기 위해서 함수 내부에 함수를 작성할 수 있게 해준다.
def test():
    def test2():
        print("inner")
    test2()
    print("outer")

if __name__ == "__main__":
    test()

코틀린에서도 파이썬처럼 로컬 함수(함수 내부에 중첩된 함수)를 사용할 수 있다.
class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) {
    fun validate(value: String, fieldName: String) { // 로컬 함수
        if (value.isEmpty()) {
            throw IllegalArgumentException("Can't save user ${user.id}: empty $fieldName")
        }
    }
    
    validate(user.name, "Name")
    validate(user.address, "Address")
}
로컬 함수는 자신이 속한 바깥 함수의 모든 파라미터와 변수를 사용할 수 있기 때문에
로컬 함수에 user 객체를 별도로 넘기지 않아도 된다.

Categories:

Updated:

Comments