[Kotlin in Action] 8. 코틀린 함수 호출 (매개변수 이름 명시, 디폴트 파라미터, 최상위 함수(유틸리티 클래스 없애기), 최상위 프로퍼티)

반응형
728x90
반응형

코틀린 함수 이름 명시

val list = listOf(1, 2, 3)
println(list) // [1, 2, 3]

 

만약 (1; 2; 3) 처럼 원소 사이를 세미콜론(;)으로 구분하고 괄호로 리스트를 둘러싸고싶다면?
(1; 2; 3)

코틀린은 이런 요구 사항을 처리할 수 있는 함수가 표준 라이브러리에 이미 들어있다. 코틀린이 지원하는 기능을 사용하지 않고 직접 구현해보자.

 

 

직접 구현하기

/*
이 함수는 어떤 타입의 값을 원소로 하는 컬렉션이든 처리할 수 있다.
 */
fun <T> joinToString(
    collection: Collection<T>,
    separator: String,
    prefix: String,
    postfix: String
): String {
    val result = StringBuilder(prefix)

    for ((index, element) in collection.withIndex()) {
        if (index > 0) result.append(separator)  // 첫 원소 앞에는 구분자를 붙이면 안된다.
        result.append(element)
    }

    result.append(postfix)

    return result.toString()
}

 

1) joinToString() : 컬렉션의 원소를 StringBuilder의 뒤에 덧붙인다.
이때 원소 사이에 구분자(separator)를 추가하고, StringBuilder의 맨 앞과 맨 뒤에는 접두사(prefix), 접미사(postfix)를 추가한다.

println(joinToString(list, "; ", "(", ")")) // (1; 2; 3)

 

 

2) 위 4개의 인자를 보자.
함수의 시그니처를 바로 파악하기 어려우며, 함수 호출 코드 자체가 모호하다. 이런 문제는 특히 불리언 플래그(flag) 값을 전달해야 하는 경우 흔히 발생한다. 이를 해결하기 위해 일부 자바 코딩 스타일에서는 불리언 대신 enum 타입을 사용하라고 권장한다.

코틀린에서는 함수에 전달하는 인자 중 일부의 이름을 명시할 수 있다. 호출시 인자 중 어느 하나라도 이름을 명시하고 나면 혼동을 막기위해 그 뒤에 오는 모든 인자는 이름을 꼭 명시해야한다.

println(joinToString(collection = list, separator = "; ", prefix = "(", postfix = ")"))

 

 

디폴트 파라미터

자바에서는 일부 클래스에서 오버로딩한 메서드가 너무 많아진다는 문제가 있다. 코틀린에서는 함수 선언 시에 파라미터의 디폴트 값을 지정할 수 있으므로 이런 오버로드 중 상당수를 피할 수 있다.

 

디폴트 값을 사용하여 joinToString 함수를 개선해보자. 대부분의 경우 아무 접두사나 접미사 없이 콤마로 원소를 구분한다. 그런 값을 디폴트로 지정하자.

fun <T> defaultJoinToString( // default 값이 지정된 파라미터들
    collection: Collection<T>,
    separator: String = ", ",
    prefix: String = "",
    postfix: String = ""
): String {
    val result = StringBuilder(prefix)

    for ((index, element) in collection.withIndex()) {
        if (index > 0) result.append(separator)  // 첫 원소 앞에는 구분자를 붙이면 안된다.
        result.append(element)
    }

    result.append(postfix)

    return result.toString()
}

 

매개변수의 일부 생략이 가능하다.

fun main() {
    val list = listOf(1, 2, 3)

    // 함수를 호출할때 모든 인자를 쓸 수도 있고, 일부를 생략할 수도 있다.
    println(defaultJoinToString(list, ", ", "", "")) // 1, 2, 3
    println(defaultJoinToString(list)) // 1, 2, 3
    println(defaultJoinToString(list, "; ")) // 1; 2; 3
}

 

 

최상위 함수로 선언하기

다양한 정적 메서드를 모아두는 역할만 담당하여, 특별한 상태나 인스턴스 메서드는 없는 클래스가 생겨난다.
JDK의 Collections 클래스가 전형적인 예다. 코틀린에서는 이런 무의미한 클래스가 필요 없다. 대신 함수를 직접 소스 파일의 최상위 수준, 모든 다른 클래스의 밖에 위치시키면 된다.

// Test.kt
fun test() {
    println("test")
}

 

그런 함수들은 여전히 그 파일의 맨 앞에 정의된 패키지의 멤버 함수다. 다른 패키지에서 그 함수를 사용하고 싶을때는 그 함수가 정의된 패키지를 임포트해야만 한다.


JVM이 클래스 안에 들어있는 코드만을 실행할 수 있기 때문에 컴파일러는 이 파일을 컴파일할 때 새로운 클래스를 정의해준다. 코틀린만 사용하는 경우에는 그냥 그런 클래스가 생긴다는 사실만 기억하면 된다.

 

코틀린의 모든 최상위 함수는 이 클래스의 정적인 메서드가 된다.

 

JAVA의 경우
pakage strings;

public class JoinKt {
    public static String joinToString(...) {
        ...
    }
}

 

예제 구현해보기

fun <T> defaultJoinToString2( // default 값이 지정된 파라미터들
    collection: Collection<T>,
    separator: String = ", ",
    prefix: String = "",
    postfix: String = ""
): String {
    val result = StringBuilder(prefix)

    for ((index, element) in collection.withIndex()) {
        if (index > 0) result.append(separator)  // 첫 원소 앞에는 구분자를 붙이면 안된다.
        result.append(element)
    }

    result.append(postfix)

    return result.toString()
}

 

호출
fun main() {
    val list = listOf(1, 2, 3)
    val result = chapter3_함수_정의와_호출._4_함수를_호출하기_쉽게만들기.defaultJoinToString2(list)
    println(result) // 1, 2, 3
}

 

클래스 이름을 변경하고 싶다면 @JvmName 어노테이션을 추가해라. (파일의 맨 앞, 패키지 이름 선언 이전에)

@file:JvmName("testClazz") // import test.testClazz;

package test

fun <T> defaultJoinToString2( // default 값이 지정된 파라미터들
    collection: Collection<T>,
    separator: String = ", ",
    prefix: String = "",
    postfix: String = ""
): String {
    val result = StringBuilder(prefix)

    for ((index, element) in collection.withIndex()) {
        if (index > 0) result.append(separator)  // 첫 원소 앞에는 구분자를 붙이면 안된다.
        result.append(element)
    }

    result.append(postfix)

    return result.toString()
}

 

 

최상위 프로퍼티

함수와 마찬가지로 프로퍼티도 파일의 최상위 수준에 놓을 수 있다. 어떤 데이터를 클래스 밖에 위치시켜야 하는 경우는 흔하지 않지만, 가끔 유용할때가 있다.

package chapter3_함수_정의와_호출._4_함수를_호출하기_쉽게만들기

/*
함수와 마찬가지로 프로퍼티도 파일의 최상위 수준에 놓을 수 있다.
어떤 데이터를 클래스 밖에 위치시켜야 하는 경우는 흔하지 않지만, 가끔 유용할때가 있다.
 */
var opCount = 0 // 최상위 프로퍼티 선언
const val constOpCount = 0 // 최상위 프로퍼티 선언  (=public static final)
// java = public static final String constOpCount = 0

fun performOperation() {
    opCount++ // 최상위 프로퍼티의 값을 변경한다.
}

fun reportOperationCount() {
    println("result $opCount") // 최상위 프로퍼티의 값을 읽는다.
}

 

 

반응형

Designed by JB FACTORY