코틀린의 표준 라이브러리에는 범위 지정 함수(Scope functions) 라는 함수가 있습니다. 많은 개발자들이 범위 지정 함수(let, run, with, apply, also) 를 사용하긴 하지만 동작 방식이 비슷해서 정확히 어떤 부분이 다른지, 적절하게 사용하고 있는지 등을 잘 모르고 사용하는 경우가 많은 거 같습니다.
이러한 고민을 해결하고자 각 함수들의 특징과 사용 방법에 대해 알아보겠습니다.
Nullable한 객체를 안전하게 처리하고, 객체를 람다 함수의 인자로 전달하여 내부에서 안전하게 사용할 수 있습니다.
public inline fun <T, R> T.let(block: T -> R): R
val nullableValue: String? = "Hello"
nullableValue?.let {
println("The value is not null: $it")
// 여기에 추가 작업 수행 가능
}
val nullableString: String? = "123"
val convertedInt: Int? = nullableString?.let {
try {
it.toInt()
} catch (e: NumberFormatException) {
null
}
}
println("Converted Int: $convertedInt")
val someValue: String? = "Hello"
someValue?.let { value ->
println("Length of the value: ${value.length}")
// 여기에 추가 작업 수행 가능
}
// 여기에서는 value에 접근할 수 없음
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")
}
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")
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")
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)
}
이러한 함수들을 적절하게 사용하면 코드의 가독성을 향상시키고 작업을 간단하게 수행할 수 있습니다.
참고
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