Language/Kotlin

[KotlinInAction] 5장-람다로 프로그래밍(5) 수신 객체 지정 람다: with 와 apply

개랭갱깽스타 2021. 12. 8. 07:31

수신 객체 지정 람다 labda with receiver

수신 객체를 명시하지 않고 람다의 본문 안에서 다른 객체의 메소드를 호출할 수 있게 한다.

(=어떤 객체의 이름을 반복하지 않고, 그 객체에 대해 다양한 연산을 수행할 수 있다!)

 

수신 객체지정 람다 중 하나인, with 와 apply 에 대해 알아볼까요?

 

.1 with

문법

inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block()
}

receiver = 수신 객체, block = 수신 객체 지정 람다

수신 객체 지정 람다의 실행 결과를 반환합니다.

 

아래 결과가 나오는 코드를 3가지 방법으로 나열해 보면,

ABCDEFGHIJKLMNOPQRSTUVWXYZ
Now I know the alphabet!

with 함수 적용 전,

fun alphabet(): String {
    val result = StringBuilder()

    for(letter in 'A'..'Z') {
        result.append(letter)
    }

    result.append("\\\\nNow I know the alphabet!")
    return result.toString()
}
//5.16 알파벳 만들기

with 함수 적용 후 - this 사용

fun alphabet() :String {
    val stringBuilder = StringBuilder()
    return with(stringBuilder) {
        for(letter in 'A'..'Z') {
            this.append(letter)
        }
        append("\\\\nNow I know the alphabet!")
        this.toString()
    }
}
//5.17 with 를 사용해 알파벳 만들기

with 함수 적용 후 - this 생략 & 식을 본문으로 하는 함수

fun alphabet() = with(StringBuilder()) {
    for (letter in 'A'..'Z') {
        append(letter)
    }
    append("\\nNow I know the alphabet!")
    toString()
}
//5.18 with 와 식을 본문으로 하는 함수를 활용해 알파벳 만들기

 

수신 객체 지정 람다 VS 확장함수

확장 함수에서의 this - 그 함수가 확장하는 타입의 **인스턴스, .**this 생략 가능

 

메소드 이름 충돌

이름이 같은 메소드가

  • with 에게 인자로 넘긴 객체의 클래스
  • with 를 사용하는 코드가 들어있는 클래스 안에

에 있다면?

with 에게 인자로 넘긴 객체의 클래스 의 함수가 실행된다.

+) OuterClass 에 정의된 함수를 사용하고 싶다면? this 참조 앞에 레이블을 붙이면 됩니다.

class MethodNamingConflicts {
    override fun toString(): String {
        print("this is method in class")
        return "this is method in class"
    }

    fun alphabet() :String {
        val stringBuilder = StringBuilder()
        return with(stringBuilder) {
            for(letter in 'A'..'Z') {
                append(letter)
            }
            append("\\\\nNow I know the alphabet!")
            toString()

            this@MethodNamingConflicts.toString()
        }
    }
}
this is method in classthis is method in class

 

.2 apply

문법

inline fun <T> T.apply(block: T.() -> Unit): T {
    block()
    return this
}

block = 수신 객체 지정 람다

수신 객체를 반환합니다.

 

apply 적용 - 확장 함수로 정의

fun alphabet() = StringBuilder().apply {
    for(letter in 'A'..'Z') {
        append(letter)
    }

    append("\\nNow I know the alphabet!")
}.toString()
//5.19 apply 를 사용해 알파벳 만들기

 

언제 쓸까?

객체의 인스턴스를 만들면서 즉시 프로퍼티 중 일부를 초기화 해야 하는 경우

Java (라이브러리 내의) Builder 객체가 이런 역할 담당

Kotlin (라이브러리의 지원 없이) 그 클래스 인스턴스에 대해 apply 활용

fun createViewWithCustomAttributes(context: Context) {
    TextView(context).apply {
        text = "Sample Text"
        textSize = 20.0f
        setPadding(10, 0, 0, 0)
    }
}
//5.20 apply 를 TextView 초기화에 사용하기

원하는대로 TextView 의 메소드를 호출 or 프로퍼티를 설정

 

buildString

수신객체지정람다만 인자로 받는다. (수신 객체 = 항상 StringBuilder)

fun alphabet() = buildString {
    for(letter in 'A'..'Z') {
        append(letter)
    }

    append("\\\\nNow I know the alphabet!")
}
//5.21 buildString 으로 알파벳 만들기

 

PREVIEW) chapter 11. DSL

영역 특화 연어 DSL Domain Specific Language

  • 수신 객체 지정 람다를 DSL 정의에 사용하는 방법
  • 수신 객체 지정 람다를 호출하는 함수를 직접 작성하는 방법

을 배운다고 합니다.

 

참고.

https://medium.com/@limgyumin/코틀린-의-apply-with-let-also-run-은-언제-사용하는가-4a517292df29

반응형