Kotlin의 Scope Funtion에 대해 가끔 헷갈리는 경우가 있어 정리하고 합니다.
영역 함수 (Scope Function)
코틀린 표준 라이브러리는 객체의 컨텍스트 내에서 어떤 식, 코드 블록을 실행할 수 있는 몇 가지 함수를 제공합니다. 제공된 람다 식을 사용하여 객체에서 이러한 함수를 호출하면 임시 범위를 형성합니다. 이러한 함수를 스코프 함수라고 합니다. 영역 함수의 기본적인 역할은 함수의 인자로 전달된 람다를 간단하게 실행해주는 것입니다.
Kotlin에서 제공하는 영역함수은 5가지를 표준으로 제공하고 있습니다.
- run
- let
- with
- apply
- also
위 표준 영역함수을 정리해보고 사용하는 방법을 간단히 정리하려 합니다. 참고로 모든 영역함수은 Inline 함수이므로 런타임에 부가적인 로드는 발생하지 않습니다. 개발자의 목적에 맞는 영역함수을 선택하기 위해서는 각 영역함수에 대한 차이점을 이해해야 합니다.
Oject Reference를 it을 사용하느냐, this를 사용하느냐와 Return value가 Lambda result인지 Context Object인지에 따라 크게 나뉩니다. 그럼 경우에 수가 4개인데 왜 Scope 함수가 4개냐면, with가 extension 형티가 아니면서, context object를 인자로받는 함수로 추가 정의되어 있습니다.
Scope Function | Object Reference | Return Value | Is extension function |
---|---|---|---|
let | it | Lambda result | Yes |
run | this | Lambda result | Yes |
run | - | Lambda result | No : Called with out the context object |
with | this | Lambda result | No : takes the context object as an argument |
apply | this | Context object | Yes |
also | it | Context object | Yes |
(참고 : https://kotlinlang.org/docs/scope-functions.html#function-selection)
It과 this의 차이는 무엇인가?
그럼 Object Reference로 It과 this를 지원한다고 하는데 두 개의 차이가 무엇인지 정리하고 넘어가겠습니다. 영역함수의 람다 내에서 컨텍스트 객체는 실제 이름 대신 짧은 참조로 사용할 수 있습니다. 각 영역함수는 람다 리시버(this) 또는 람다 인자(it)의 두 가지 방법 중 하나를 사용하여 컨텍스트 개체에 접근합니다. 두 가지 모두 동일한 기능을 제공하므로 서로 다른 사례에 대한 장단점을 설명하고 사용 권장 사항을 제공합니다.
this
run, with 및 apply는 this 키워드로 컨텍스트 개체를 람다 수신기로 참조합니다. 따라서 람다에서 개체는 일반 클래스 함수에서와 같이 사용할 수 있습니다. 대부분의 경우 수신자 객체의 멤버에 액세스할 때 이를 생략하여 코드를 더 짧게 만들 수 있습니다. 반면 생략하면 수신자 멤버와 외부 객체 또는 기능을 구분하기 어려울 수 있다. 따라서 컨텍스트 개체를 수신기(this)로 사용하는 것은 주로 개체 멤버에 대해 작동하는 람다에 권장됩니다. 해당 기능을 호출하거나 속성을 할당합니다.
val adam = Person("Adam").apply {
age = 20
city = "London"
}
println(adam)
it
let, also는 컨텍스트 개체를 람다 인수로 사용합니다. 인수 이름을 지정하지 않으면 암시적 기본 이름인 it으로 개체에 액세스합니다. 이보다 짧고 일반적으로 이 표현이 포함된 표현은 읽기 쉽습니다. 그러나 개체 함수 또는 속성을 호출할 때 this와 같이 암시적으로 개체를 사용할 수 없습니다. 따라서 객체가 대부분 함수 호출에서 인수로 사용되는 경우 컨텍스트 객체를 그대로 사용하는 것이 좋습니다. 코드 블록에서 여러 변수를 사용하는 경우에도 좋습니다.
fun getRandomInt(): Int {
return Random.nextInt(100).also {
writeToLog("getRandomInt() generated value $it")
}
}
val i = getRandomInt()
println(i)
이렇게 정리해도 어려운 것 같습니다. 각 함수를 하나씩 살펴보면서 사용방법을 정리해야겠습니다. 각 함수를 살펴보기전에 공식 가이드에서 함수를 선택하기 위해 고려하는 방법입니다.
- Executing a lambda on non-null objects: let
- Introducing an expression as a variable in local scope: let
- Object configuration: apply
- Object configuration and computing the result: run
- Running statements where an expression is required: non-extension run
- Additional effects: also
- Grouping function calls on an object: with
let
- context object : it
- return value : lambda result
let은 결과에 대해 하나 이상의 함수를 호출하는 데 사용할 수 있습니다.
val numbers = mutableListOf("one", "two", "three", "four", "five")
val resultList = numbers.map { it.length }.filter { it > 3 }
println(resultList)
예를 들어 위의 코드를 아래와 같이 수정을 할 수 있습니다.
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let {
println(it)
// and more function calls if needed
}
또 다른 경우에는 Non-Null 객체에 대해 오직 수행할 코드블럭에 사용합니다. null이 아닌 개체에 대해 작업을 수행하려면 연산자 ?를 사용합니다. 람다에 있는 작업으로 let을 호출합니다.
val str: String? = "Hello"
//processNonNullString(str) // compilation error: str can be null
val length = str?.let {
println("let() called on $it")
processNonNullString(it) // OK: 'it' is not null inside '?.let { }'
it.length
}
'Android > Kotlin' 카테고리의 다른 글
[Kotlin] Flow<List<T>> 에서 List<T>로 변환하기. (0) | 2022.09.15 |
---|---|
[Kotlin/Plugin] Json으로 data class 추출하기 (0) | 2022.07.28 |
[Kotlin] Object (0) | 2022.06.22 |
[Kotlin] @Volatile 키워드란? (0) | 2022.06.15 |
[Kotlin] Coroutine Builder 간략 정리 (0) | 2022.04.25 |