본문 바로가기

코딩/안드로이드

Kotlin DSL을 이해해보자 - 1. 확장함수타입

TL; DR

 

DSL을 만들 때 인자로 넣는 Person.() -> Unit 은 확장함수타입이다. 

확장함수를 인자로 넘겨주기 때문에 간략한 코드를 만들 수 있다.


 

 

Kotlin DSL을 간단하게 말하자면 gradle의 dependencies를 보면 된다

 

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version")
    implementation("org.koin:koin-android:1.0.2")
    .
    .
    .
 }

이렇게 dependencies의 블록 안에 실행하고자 하는 코드를 한줄 한줄 '선언'처럼 적어두게 하여

 

'명령형'이 아닌 '선언형'으로 코딩하게 하는 것이다. (Kotlin in action, pg.479)

 

 

...솔직히 이렇게 이해하는 것 보다는..

Builder/Observer/Setter...와 같은 복잡해질 수 있는 코드를

힙하고 간략한 코드로 만들어주는 마법과도 같은 테크닉...

으로 이해하는게 난 더 기분이 좋다 ㅎ

 

 

그렇다면 바로 간단하게 kotlin dsl을 만들어보자

 

예제코드 : https://github.com/5seunghoon/Kotlin_DSL_Example


먼저, 간단한 Person클래스를 만들자

 

data class Person(var age: Int = 0, var name: String = "java") 

 

간단하다.

 

그 다음, Person을 만드는 DSL을 만들자

 

    fun makePerson(
        makePersonAction: (Person) -> Unit
    ): Person {
        val person = Person()
        makePersonAction(person)
        return person
    }

makePerson의 인자는 람다이다. 

Person이 인자가 되는 람다인데, 

이렇게 람다를 인자로 주고, 함수의 내용에선 그 람다를 실행한다.

 

이 함수는 다음과 같이 사용하게 된다.

    println(makePerson {
        it.age = 10
        it.name = "kotlin"
    })

이렇게 makePerson의 인자로 it.age, it.name을 설정하는 람다를 넣어주면 

10살에 이름은 코틀린인 사람이 만들어진다

 

하지만 불편한 점이 한가지 있는데, 바로 it을 계속 쓴다는 점이다.

 

이것을 없애기 위해선 makePerson을 다음과 같이 만들어 주면 된다.

 

    fun makePerson(
        makePersonAction: Person.() -> Unit
    ): Person {
        val person = Person()
        person.makePersonAction()
        return person
    }

 

잘보면 인자에 들어가는 람다의 형태가 약간 달라졌다.

(Person) -> Unit

이 아니라

Person.() -> Unit

이다.

 

뭔가 형태가 묘하게 본적이 있는것 같다...하시다면 아마 그건 '확장함수'일 것이다.

 

그렇다! 이것은 확장함수타입을 넘겨주는 것이다!

 

makePerson의 인자로 Person의 확장함수가 될 람다를 넘겨주면

makePerson의 내부에선 person.makePersonAction()처럼 확장함수의 형태로 활용하게 된다.

 

이 함수는 아래와 같이 사용한다

 

    println(V2.makePerson {
        //확장함수처럼 this가 넘어가기 때문에 it을 붙이지 않아도 된다
        age = 10
        name = "kotlin"
    })

이렇게 it을 붙이지 않아도 된다. 

 

인자로 들어가는 람다는 person의 확장함수이기 때문에

age = 10

의 원형은

this.age = 10

인 것이고, 여기서 this가 생략되게 되어 깔끔한 코드가 나오게 된다.

 

 


 

심화

 

이런 확장함수타입은 run, apply에서도 활용된다.

 

run의 구현을 살펴보자.

 

public inline fun <T, R> T.run(block: T.() -> R): R = block()

 

정말 몇번을 봐도 깔끔하고 힙한 코드다..

 

여튼, run의 인자를 보면 block이라는 이름으로 T.() -> R 타입의 확장함수타입이 있는 것을 볼 수 있다.

 

즉, run안에 들어간 람다가 T의 확장함수가 되는 것이고, 대충 그것을 실행하게 된다.

 

이렇게 구현되어 있기 때문에 run 안에선 run을 이용하는 객체가 this로 들어가게 되는 것이고,

따로 별다른 것을 적어주지 않아도 run을 이용하는 객체가 주인공이 되어 run안에서 활약하는 것이다.

 

 

 

'코딩 > 안드로이드' 카테고리의 다른 글

헤더가 찰싹 달라 붙는 스크롤 뷰 (Sticky Scroll View)  (4) 2019.09.28
Android DiffUtil  (0) 2019.09.12
Gradle : implementation VS compileOnly  (0) 2019.08.03
RxJava와 Room DB  (0) 2019.05.26
Android Action Mode  (0) 2019.05.13