let, also, apply, run, with의 차이

na.ram·2023년 11월 24일
1

Kotlin

목록 보기
1/3
post-thumbnail

let

let은 함수를 호출하는 객체 Tblock의 인자로 넘기고 block의 결괏값 R을 반환합니다.

public inline fun <T, R> T.let(block: (T) -> R): R { ... return block(this) }

let의 매개변수로는 람다식 형태인 block이 있고, T를 매개변수로 받아 R을 반환합니다. 함수 본문의 this는 객체 T를 가리킵니다.

Custom View에서 let() 함수 활용

val padding = TypedValue.applyDimension(
    TypedValue.COMPLEX_UNIT_DIP,
    16F,
    resources.displayMetrics
).toInt()

tvHelloWorld.setPadding(padding, 0, padding, 0)

위의 코드에서처럼 padding이 한 번만 사용되면 변수 할당을 하느라 자원 낭비가 있을 수 있습니다.

이때 아래와 같이 let() 함수를 사용할 수 있습니다.

TypedValue.applyDimension(
    TypedValue.COMPLEX_UNIT_DIP,
    16F,
    resources.displayMetrics
).toInt().let { padding->
    tvHelloWorld.setPadding(padding, 0, padding, 0)
}

📌 정리

non-null 객체에 대해 람다식을 실행하거나 local scope 에서 변수로 표현식을 소개하기 위해 사용합니다.

also

also는 함수를 호출하는 객체 Tblock에 전달하고 객체 T 자체를 반환합니다.

public inline fun <T> T.also(block: (T) -> Unit): T = { block(this); return this }

also블록 안의 코드 수행 결과와 상관없이 this(객체 T)를 반환하게 됩니다.

also는 람다식이 본문을 처리하지만 마지막 표현식이 b에 할당되는 것이 아닌 person 객체 자신에 할당됩니다. 따라서 bPerson의 객체 person을 반환하고 새로운 객체 b가 할당되어 만들어집니다.

fun main() {
    data class Person(var name: String, var skills: String)
    var person = Person("Kildong", "Kotlin")
    val a = person.let {
        it.skills = "Android"
        "success"
    }
    println(person)
    println("a: $a")

    val b = person.also {
        it.skills = "Java"
        "success"
    }
    println(person)
    println("b: $b")
}

📌 정리

객체의 속성을 전혀 사용하지 않거나 변경하지 않고 사용하는 경우에 also를 사용합니다. 객체의 데이터 유효성을 확인하거나, 디버그, 로깅 등의 부가적인 목적으로 사용할 때에 적합합니다.

apply

apply는 함수를 호출하는 객체 Tblock으로 전달하고 객체 자체인 this를 반환합니다.

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

apply블록 안의 코드 수행 결과와 상관없이 this(객체 T)를 반환하게 됩니다.

applyalso의 다른 점은 T.()와 같은 표현에서 람다식이 확장 함수로 처리된다는 점입니다.

또한, alsoit을 사용해 멤버에 접근하고 it을 생략할 수 없지만, applythis로 받고 this를 생략할 수 있습니다.

fun main() {
    data class Person(var name: String, var skills: String)
    var person = Person("Kildong", "Kotlin")
    person.apply { this.skills = "Swift" }
    println(person)

    val returnObj = person.**apply** {
        name = "Sean"
        skills = "Java"
    }
    println(person)
    print(returnObj)
}

📌 정리

객체의 함수를 사용하지 않고 자기 자신을 다시 반환하기 때문에 주로 객체의 초기화나 변경 시에 사용됩니다.

run

run은 두 가지 형태로 선언되어 있습니다.

먼저, 인자가 없는 익명 함수처럼 동작하는 형태로 어떤 객체를 생성하기 위한 명령문을 block 안에 묶음으로써 가독성을 높이는 역할을 합니다.

public inline fun <R> run(block: () -> R): R = return block()

ex)

        val num = 10
        skills = run {
            val level = "Kotlin Level: $num"
            level // 마지막 표현식이 반환됨
        }
        println(skills)

runblock이 독립적으로 사용되어 마지막 표현식을 반환했습니다.

두 번째로는 객체에서 호출하는 형태로, 함수를 호출하는 객체 Tblock으로 전달하고 block의 결괏값 R을 반환합니다. 어떤 값을 계산할 필요가 있거나 혹은 여러 개의 지역변수의 범위를 제한할 때 사용합니다.

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

이번에는 block이 독립적으로 사용됩니다. 이어지는 block 내에서 처리할 작업을 넣어줄 수 있으며 일반 함수와 마찬가지로 값을 반환하지 않거나 특정 값을 반환할 수도 있습니다.

applythis에 해당하는 객체를 반환하는 반면에 run은 마지막 표현식이나 혹은 마지막 표현식이 없다면 Unit을 반환하는 차이점이 있습니다.

fun main() {
    data class Person(var name: String, var skills: String)
    var person = Person("Kildong", "Kotlin")
    val returnObj = person.apply {
        name = "Sean"
        skills = "Java"
        "Success" // 사용되지 않음
    }
    println(person)
    println(returnObj) // Person("Sean", "Java") 출력

    val returnObj2 = person.**run** {
        this.name = "Dooly"
        this.skills = "C#"
        "Success"
    }
    println(person)
    println(returnObj2) // Success 출력
}

📌 정리

익명 함수의 형태로는 표현식이 필요한 곳에서 statement를 실행하기 위해 사용하고, 객체를 호출하는 형태로는 객체를 구성하고, 결과를 계산하기 위해 사용합니다.

with

with는 인자로 받는 객체를 blockreceiver로 전달하며 결괏값을 반환합니다. withrun과 기능이 거의 동일한데, run의 경우 receiver가 없지만 withreceiver로 전달할 객체를 처리하므로 객체의 위치가 달라집니다.

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

with는 매개변수가 두 개이므로 with() {...}와 같은 형태로 넣어줍니다. with는 확장 함수 형태가 아니고 단독으로 사용되는 함수이며 세이프 콜(?.)을 지원하지 않기 때문에 let과 같이 사용되기도 합니다.

또한 letwith의 표현을 병합하면 run으로도 표현할 수 있습니다.

ex)

fun main() {
    data class User(var name: String, var skills: String, var email: String? = null)
    val user = User("Kildong", "default")

    val result = with(user) {
        skills = "Kotlin"
        email = "Kildong@example.com"
        // 표현식이 없기 때문에 Unit 반환
    }
    println(user)
    println(result)
}

📌 정리

객체에 대해 여러 개의 함수를 호출할 때 그룹화하는 용도로 사용합니다.

그 외

자료 참조

Do it! 코틀린 프로그래밍 | 저자 : 황영덕
[Kotlin] 코틀린 let, with, run, apply, also 차이 비교 정리

0개의 댓글

관련 채용 정보