Programming Language/Kotlin

[Kotlin] Scope Function 정리 (let, run, with, apply, also)

Tenacity_Dev 2023. 1. 1. 16:26
728x90

스코프 함수란?
코틀린에서는 Object Context 내에서 코트 블록을 실행하는 것이 목적인 여러 함수가 존재한다.
제공된 람다식을 사용하여 객체에 이러한 함수들을 호출하면 임시적으로 스코프(범위)가 설정된다. 이 범위에서는 해당 객체의 이름없이 접근할 수있다.

스코프 함수에는 5가지의 함수가 존재하는데,
let, run, with, apply, also

공통점으로는 객체에 붙어있는 코드 블록을 실행시킨다는 것이다.

이에 차이점과 각 함수의 특징들에 대해서 정리해보자.

5가지의 함수의 정의이다.

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

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

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

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

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

1. let

  • let 함수는 확장 함수이기 때문에 context object를 receiver(this)로 전달한다. 그러나, 코드 블럭내에서 this를 파라미터로 입력하기 때문에 it을 사용해 프로퍼티에 접근할 수 있다.
  • let 함수는 반환 결과가 람다의 결과이다.
  • let함수는 지정된 값이 null이 아닌 경우에 코드를 실행해야 하는 경우, Nullable 객체를 다른 Nullable 객체로 변환하는 경우, 단일 지역 변수의 범위를 제한하는 경우에 유용하다.
getMember()?.let {
                    // null이 아닐때만 실행
    println(it)     // it: member
}

2. run

  • run 함수는 확장함수이기 때문에 context object를 receiver(this)로 이용할 수 있다.
  • run 함수는 반환결과가 람다의 결과이다.
  • run 함수는 객체의 초기화와 리턴 값의 계산을 람다가 포함할 때 유용하다.
  • 확장함수이기 때문에 safe call(.?)을 붙여 non-null일 때에만 실행할 수 있다.
  • 주로 어떤 값을 계산할 필요가 있거나 여러개의 지역변수 범위를 제한할 때 사용한다.
data class Member(val name: String, var age: Int)

val member = Member("Wangi", 26)
val nextYearHisAge = member.run {
    ++age               // this.age
}

println(nextYearHisAge) // 27

val member = run {
    val name = "Wangi"
    val age = 26
    Member(name, age)
}

rum은 단지 어떤 객체를 생성하여 명령문을 블록 안에 적음으로써 가독성을 높이는 역할을 한다.

3. with

  • with 함수는 확장 함수가 아니기 때문에 context object를 argument로 전달한다. 그러나, 람다의 내부에는 확장 함수로 적용되어서 this가 사용 가능하다.
  • with함수는 반환결과가 람다의 결과이다.
  • with 함수는 수신객체가 non-nullable이고, 결과가 필요하지 않은 경우에 유용하다.
val member = Member("Wangi", 26)
with(member) {
    println("This member name is $name") // this.name
    println("This member age is $age")   // this.age
}

4. apply

  • apply 함수는 확장 함수이기 때문에 context object를 receiver(this)로 이용할 수 있다.
  • apply 함수는 반환 결과가 객체 자신이다. Builder 패턴과 동일한 용도로 사용된다.
  • apply 함수는 객체의 프로퍼티 만을 사용하는 경우가 많으며, 대표적인 사례는 객체의 초기화이다.
val member = Member("Wangi").apply{
    age = 26                // this.age
}

println(member)             // Member(name=Wangi, age=26)

5. also

  • also 함수는 확장 함수이기 때문에 context object를 receiver(this)로 전달한다. 그러나, 코드 블럭 내에서 this를 파라미터로 입력하기 때문에 it을 사용해 프로퍼티에 접근할 수 있다.
  • also 함수는 반환 결과가 객체 자신이다. Builder 패턴과 동일한 용도로 사용된다. (Bulider 패턴은 나중에 디자인패턴을 정리할때 설명하겠다.)
  • also 함수는 객체의 속성을 전혀 사용하지 않거나 변경하지 않고 사용하는 경우에 유용하다. 예를 들면, 객체의 데이터 유효성을 확인하거나, 디버그, 로깅 등의 부가적인 목적으로 사용할 때에 적합하다.
class Membership(member: Member) {
    val member = member.also {
        requireNotNull(it.age)
        println(it.name)
    }
}


참고 :
https://kotlinlang.org/docs/scope-functions.html
코틀린 공식문서를 보고 정리하였습니다.

728x90