코틀린에서 자바 API를 수정하지 않으면서 코틀린의 편의성을 제공하려면 어떻게 해야 했을까요? 코틀린은 확장(Extension)이라는 방식을 통해 이를 해결했습니다.

 

fun String.lastChar(): Char = this[this.length - 1]

println("hello".lastChar())  // o

확장함수는 어떤 클래스의 메소드인 것처럼 호출할 수 있지만, 밖에서 선언된 함수입니다.

문법은 함수 선언과 거의 같은데, 함수명 앞에 수신 객체 타입(receiver type)을 적어주고, 함수에서는 this로 수신 객체(receiver object)를 사용하는 것만 다릅니다.

 

fun String.lastChar(): Char = this[length - 1]

this는 생략도 가능합니다

 

동작방식을 아는게 약간 중요한데, 확장 함수는 정적 메소드의 문법 편의(syntatic sugar)입니다.

따라서 public 메소드는 사용 가능하지만 private, protected 멤버는 접근 불가능하고, 클래스가 아닌 구체적 타입을 receiver type으로 지정하는 것은 가능합니다.

오버라이드할 수도 없습니다

만약 확장함수와 멤버함수의 시그니처가 같다면 멤버함수가 호출됩니다 (중요)

 

fun Collection<String>.join(
    separator: String = ", ",
    prefix: String = "",
    postfix: String = "",
): String {
    val result = StringBuilder(prefix)
    for ((index, value) in this.withIndex()) {
        if (index > 0) result.append(separator)
        result.append(value)
    }
    result.append(postfix)
    return result.toString()
}

println(listOf("vince", "paul", "jo").join())   // OK
//println(listOf(1, 2, 3).join())               // NO

예제가 약간 잘못된 것 같긴 한데; Collection<String>에 대해서만 확장함수를 만든 예시입니다. Collection<Int>에는 동작하지 않는 걸 확인할 수 있습니다.

 

Reference

- https://kotlinlang.org/docs/extensions.html

반응형