Primary/Kotlin

[Kotlin] 클래스 안에 확장 함수?

해스끼 2022. 6. 16. 14:33

Kotlin의 확장 함수는 보통 파일의 최상위 레벨에서 정의된다. 그런데 확장 함수를 클래스 안에서 정의할 수도 있다. 사실 어디서나 정의할 수 있다.

 

어쨌든, 클래스 안에 확장 함수를 정의하면 뭐가 좋을까?

확장 함수를 품은 클래스

클래스 ``A``에서 선언된 확장 함수는 ``A`` 내부 또는 ``A``를 receiver로 받는 람다식 안에서만 사용될 수 있다. 

 

Receiver가 무엇인지 궁금하다면 다음 글을 참고하자.

 

코틀린(Kotlin) - lambda with receiver(수신 객체 지정 람다) : with, apply

lambda with receiver (수신 객체 지정 람다) 개발을 하다 보면 객체를 반복 사용하면서 그 객체에 대한 연산을 명시하는 경우가 많습니다. 수신 객체 지정 람다는 이러한 수신 객체를 반복적으로 명시

0391kjy.tistory.com

다음 코드를 보자.

class Table {
    fun <T> Column<T>.primaryKey(): Column<T>
    fun Column<Int>.autoIncrement(): Column<T>
}

``Column<T>.primaryKey()`` 함수는 ``Column<T>``의 확장 함수이지만. ``Table`` 클래스 안에서 정의됐기 때문에 ``Table`` 안에서 또는 ``Table``을 receiver로 받는 람다식 안에서만 호출될 수 있다. 이렇게.

val someLambda: Table.() -> Unit = {
    // receiver is Table
    val id = Column<Int>().primaryKey().autoIncrement()
}

달리 말하면, ``Table``의 맥락 안에서만 호출할 수 있다는 뜻이다. 

그래서요?

확장 함수를 특정 맥락 안에서만 사용할 수 있다는 점에 주목하자. 예를 들어 ``FileReader``에서는 ``File``을 쓸 수 없고 ``FileWriter``에서만 쓸 수만 있게 하고 싶다면, ``FileWriter`` 안에 ``File.write()``확장 함수를 구현하면 된다.

 

또 다른 예시로, Jetpack Compose UI에는 특정 범위(``BoxScope``, ``RowScope`` 등)에서만 사용할 수 있는 modifier가 있다. 이러한 modifier 역시 해당 scope 내부에 확장 함수로 정의되어 있다. 이처럼 확장 함수와 receiver를 잘 조합하면 깔끔하고 직관적인 DSL을 작성할 수 있다.

 

자신만의 DSL을 정의하고 싶은 사람이라면, Kotlin의 함수형 프로그래밍 기능을 숙지하자.