[Kotlin] 범위 지정 함수 let, run, with, apply, also 알아보기

WWWKR·2024년 2월 22일
0

코틀린의 표준 라이브러리에는 범위 지정 함수(Scope functions) 라는 함수가 있습니다. 많은 개발자들이 범위 지정 함수(let, run, with, apply, also) 를 사용하긴 하지만 동작 방식이 비슷해서 정확히 어떤 부분이 다른지, 적절하게 사용하고 있는지 등을 잘 모르고 사용하는 경우가 많은 거 같습니다.

이러한 고민을 해결하고자 각 함수들의 특징과 사용 방법에 대해 알아보겠습니다.

let

Nullable한 객체를 안전하게 처리하고, 객체를 람다 함수의 인자로 전달하여 내부에서 안전하게 사용할 수 있습니다.

public inline fun <T, R> T.let(block: T -> R): R
  1. 지정된 값이 null이 아닌 경우에 코드를 실행해야 하는 경우:
val nullableValue: String? = "Hello"
nullableValue?.let {
    println("The value is not null: $it")
    // 여기에 추가 작업 수행 가능
}
  1. Nullable 객체를 다른 Nullable 객체로 변환하는 경우:
val nullableString: String? = "123"
val convertedInt: Int? = nullableString?.let {
    try {
        it.toInt()
    } catch (e: NumberFormatException) {
        null
    }
}
println("Converted Int: $convertedInt")
  1. 단일 지역 변수의 범위를 제한하는 경우:
val someValue: String? = "Hello"
someValue?.let { value ->
    println("Length of the value: ${value.length}")
    // 여기에 추가 작업 수행 가능
}
// 여기에서는 value에 접근할 수 없음

run

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

'run' 함수는 객체의 범위를 지정하고 그 범위 내에서 코드를 실행합니다. 특징으로는 람다 내에서 수신 객체(this)를 사용하여 작업을 수행할 수 있고, 마지막 라인을 return 합니다.

data class Person(val name: String, val age: Int)

fun main() {
    val person = Person("Alice", 30)
    
    val result = person.run {
        println("Name: $name") // person 객체의 속성에 접근
        println("Age: $age")
        age * 2 // 실행 결과 반환
    }
    
    println("Double the age: $result")
}

with

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

'with' 함수는 수신객체에 대한 작업 후 마지막 라인을 return 하며, 'run' 함수와 완전히 똑같이 동작합니다.

다른 점은, 'run' 함수는 확장함수로 사용되지만, 'with' 함수는 수신객체를 파라미터로 받아 사용한다는 점 입니다. 주로 연속적으로 객체의 멤버에 접근해야 할 때 사용됩니다.

data class Person(val name: String, val age: Int)

val person = Person("Alice", 30)
val result = with(person) {
    println("Name: $name")
    println("Age: $age")
    age * 2
}
println("Double the age: $result")

apply

public inline fun <T> T.apply(block: T.() -> Unit): T

'apply' 함수는 객체의 초기화 및 설정에 사용됩니다. 객체의 프로퍼티 값을 변경하고 그 자체를 반환합니다.

data class Person(var name: String, var age: Int)

val person = Person("Alice", 30)
val result = person.apply {
    name = "Bob"
    age = 35
}
println("Updated Person: $result")

also

public inline fun <T> T.also(block: (T) -> Unit): T

'also' 함수는 'apply' 함수와 비슷하지만 객체 자체 대신 수신 객체를 반환합니다. 보통 부수적인 효과를 처리할 때 사용됩니다.

data class Person(var name: String, var age: Int)

val person = Person("Alice", 30)
val result = person.also {
    requireNotNull(it.name)
    println(it.age)
}

요약

  • let: Nullable한 객체에 안전하게 접근하고 작업할 때 사용합니다.
  • run: 객체의 범위를 지정하고 내부에서 코드를 실행하며, 결과를 반환할 때 사용합니다.
  • with: 연속적으로 객체의 멤버에 접근해야 할 때 사용합니다.
  • apply: 객체의 초기화 및 설정에 사용합니다. 객체 자체를 반환합니다.
  • also: 객체에 대한 보조적인 작업을 수행할 때 사용합니다. 수신 객체를 반환합니다.

이러한 함수들을 적절하게 사용하면 코드의 가독성을 향상시키고 작업을 간단하게 수행할 수 있습니다.

참고
https://brunch.co.kr/@mystoryg/151
https://velog.io/@sjlim/Kotlin-%EB%B2%94%EC%9C%84-%EC%A7%80%EC%A0%95-%ED%95%A8%EC%88%98-Scope-Funciton-let-with-run-apply-also

profile
안드로이드 개발자

0개의 댓글