Scope functions(범위 지정 함수)란?
Kotlin standard library에서는 컨텍스트 객체에 대한 작업들을 블록내에서 수행하도록 하는 함수들을 포함한다. 블록 안에서는 객체의 이름없이 객체에 접근할 수 있다. 이에 따라 코드를 보다 간결하고 읽기 쉽게 만들 수 있다. 이러한 함수들을 Scope functions라고 한다.
Scope functions 종류 및 차이점
- Scope functions 종류
함수 | 컨텍스트 객체 참조 | 반환 값(전체 식의 결과) | 확장 함수 유무 |
let | it | 람다의 결과 | 예 |
run | this | 람다의 결과 | 예 |
run | - | 람다의 결과 | 아니오(컨텍스트 객체 없이 호출된다.) |
with | this | 람다의 결과 | 아니오(컨텍스트 객체를 인수로 사용한다.) |
apply | this | 컨텍스트 객체 | 예 |
also | it | 컨텍스트 객체 | 예 |
- Scope functions 차이점
- 컨텍스트 객체 참조
- this
- 수신 객체의 멤버에 접근할 때 this(또는 생략)를 사용할 수 있다.
- 생략하면 수신 객체의 멤버와 외부의 객체 또는 함수들과의 구분이 어려울 수 있다.
- 컨텍스트 객체를 수신 객체로 갖는 것은 객체의 함수를 호출하거나 프로퍼티에 값을 할당하여 객체의 구성원에 주로 작동하는 람다에 권장된다.
- it
- 컨텍스트 객체를 인수로 받기 때문에 이름을 지정하거나 it 키워드를 사용하여 멤버에 접근한다.
- 함수를 호출할 때 인수를 사용하거나 여러 변수를 사용해야 하는 람다에 권장된다.
- this
- 반환 값(전체 식의 결과)
- 컨텍스트 객체
- 컨텍스트 객체 자체를 반환하기 때문에 반환하는 객체에 연쇄 함수 호출을 잇따라서 할 수 있다.
- 컨텍스트 객체를 리턴하는 함수의 리턴문(statements)에도 사용할 수 있다.
- 람다의 결과
- 변수에 결과를 할당할 때 사용할 수 있다.
- 결과에 대한 연산을 연결하는 등의 작업에 사용할 수 있다.
- 리턴 값을 무시할 때 또는 람다 내에 임시 변수를 생성할 때 사용할 수 있다.
- 컨텍스트 객체
- 컨텍스트 객체 참조
let
inline fun <T, R> T.let(block: (T) -> R): R
- let의 코드 블록에 it을 인수로 사용하는 함수 하나만 포함 할 경우, 메소드 참조를 할 수 있다.
val numbers = mutableListOf("one", "two", "three", "four", "five")
val resultList = numbers.map { it.length }.filter { it > 3 } // (1) let을 사용하지 않은 경우
println(resultList)
numbers.map { it.length }.filter { it > 3 }.let { // (2) let을 사용 한 경우
println(it)
// 필요하다면 함수 호출들을 더 할 수 있다.
}
numbers.map { it.length }.filter { it > 3 }.let(::println) // (3) 메소드 참조를 사용 한 경우
- 객체의 널 체크가 필요할 때 사용한다.
val str: String? = "Hello"
//processNonNullString(str) - 컴파일 에러, str은 널이 될 수 있다.
val length = str?.let {
println("let() called on $it")
processNonNullString(it) - 컴파일, it은 안전한 호출(?.)안에서 널이 아님을 보장받는다.
it.length
}
- it 대신에 다른 이름을 사용하려면 해당 이름을 람다 인수로 제공하면 된다.
val numbers = listOf("one", "two", "three", "four")
val modifiedFirstItem = numbers.first().let { firstItem ->
println("The first item of the list is '$firstItem'")
if (firstItem.length >= 5) firstItem else "!" + firstItem + "!"
}.uppercase()
println("First item after modifications: '$modifiedFirstItem'")
with
- 수신 객체를 인수로 전달한다.
inline fun <T, R> with(receiver: T, block: T.() -> R): R
- 객체에 대한 함수 호출을 그룹화 시킬 때 사용한다.
val numbers = mutableListOf("one", "two", "three")
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements") // this 없이 멤버에 접근
}
run
inline fun <T, R> T.run(block: T.() -> R): R
- 람다안에서 객체를 초기화하고 블록의 마지막 라인을 리턴할 때 사용하면 좋다.
val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
port = 8080
query(prepareRequest() + " to port $port")
}
// the same code written with let() function:
val letResult = service.let {
it.port = 8080
it.query(it.prepareRequest() + " to port ${it.port}")
}
non-extension run
- 수신 객체가 없지만 람다의 결과를 반환한다.
inline fun <R> run(block: () -> R): R
- 블록 내에서 여러 문장을 실행한 후, 마지막 라인을 반환한다.
val hexNumberRegex = run {
val digits = "0-9"
val hexDigits = "A-Fa-f"
val sign = "+-"
Regex("[$sign]?[$digits$hexDigits]+")
}
for (match in hexNumberRegex.findAll("+123 -FFFF !%*& 88 XYZ")) {
println(match.value)
}
apply
inline fun <T> T.apply(block: T.() -> Unit): T
- 수신 객체의 프로퍼티를 변경할 때 사용한다. 즉 객체를 구성할 때 사용한다.
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
also
inline fun <T> T.also(block: (T) -> Unit): T
- 멤버에 접근하기 보다는 객체에 참조를 필요로 할 때 또는 객체에 효과를 더할 때 사용한다.
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
Scope functions를 사용할 때 주의할 점
- Scope functions를 남발하는 것은 코드를 읽기 어렵게 하고 오류가 발생할 수 있다.
- Scope functions를 중첩해서 사용하는 것은 피하는 것이 좋다.
- 현재의 컨텍스트 객체와 this 또는 it 의 값에 대해 혼동하기 쉽기 때문에 연쇄적으로 사용하는 것은 조심해야 한다.
'기타 > Android' 카테고리의 다른 글
[Kotlin] This expressions 란? (1) | 2023.02.27 |
---|---|
[Kotlin] Sealed class 란? (0) | 2023.02.25 |
[Android] RecyclerView 스크롤을 특정 위치로 이동 (0) | 2022.11.19 |
[Android] Toolbar 뒤로가기 버튼 만들기 (0) | 2022.08.12 |
[Android] BottomNavigationView(하단 바) 설정하기 (0) | 2022.08.10 |