코틀린 표준 라이브러리로 확장함수(extension function)들을 제공합니다.
확장함수에는 범위지정함수(scope-functions) 인 apply
, also
, with
, run
, let
이 있고, 언제 사용하면 좋을지 & 저는 어떻게 사용하는지 공유해봅니다.
블록 내에서 수신객체를 this
로 사용되고, 수신객체 자신을 리턴합니다.
inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
주로 객체를 초기화 할 때 사용하고 있습니다.
data class Person(
var name: String,
var age: Int = 0,
var city: String = ""
)
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
블록 내에서 수신객체를 it
으로 사용할 수 있고, 수신객체 자신을 리턴합니다.
inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
Debug나 Log와 같이 수신객체의 속성을 변경하지 않고 참조로 사용할 경우에 유용합니다.
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
non-nullable 한 수신객체를 블록 내에서 this
로 사용할 수 있고, 람다의 결과를 리턴합니다.
inline fun <T, R> with(receiver: T, block: T.() -> R): R {
return receiver.block()
}
중복되는 객체를 호출할 때 사용하면 코드를 간결하게 만들 수 있습니다.
val numbers = mutableListOf("one", "two", "three")
// 사용 전
println("'with' is called with argument $numbers")
println("It contains ${numbers.size} elements")
// 사용 후
with(numbers) {
println("'with' is called with argument $this")
println("It contains $size elements")
}
또한, 블록 내의 결과를 반환할 때에도 사용할 수 있습니다.
val numbers = mutableListOf("one", "two", "three")
// 사용 전
val firstAndLast = "The first element is ${numbers.first()}," +
" the last element is ${numbers.last()}"
// 사용 후
val firstAndLast = with(numbers) {
"The first element is ${first()}," +
" the last element is ${last()}"
}
println(firstAndLast)
// "The first element is one, the last element is three"
블록 내에서 수신객체를 this
로 사용되고, 람다의 결과를 리턴합니다.
inline fun <T, R> T.run(block: T.() -> R): R {
return block()
}
with 와 비슷하지만 사용하는 방식에 차이가 있습니다.
수신객체를 이용하여 어떠한 계산된 결과를 리턴하고자 할 때에 사용할 수 있습니다.
val p1 = Person("Jihye", 27, "Seoul")
val p2 = Person("Adam", 32, "London")
val isSeoul: Boolean = p1.run {
this.city == "Busan" // 결과를 리턴합니다.
}
println(isSeoul) // false
val sumAge: Boolean = run {
p1.age + p2.age
}
println(sumAge) // 59
블록 내에서 수신객체를 it
으로 사용할 수 있고, 람다의 결과를 리턴합니다.
inline fun <T, R> T.let(block: (T) -> R): R {
return block(this)
}
nullable 객체를 null이 아닌 경우에 코드를 실행해야 하는 경우 또는 가독성을 위해서도 주로 사용합니다.
val str: String? = "Hello"
val length = str?.let {
// str이 null이 아닌 경우 처리할 구문
println("let() called on $it")
it.length
}
val numbers = listOf("one", "two", "three", "four")
// 가독성을 위해
val modifiedFirstItem = numbers.first().let { firstItem ->
if (firstItem.length >= 5) firstItem
else "!" + firstItem + "!"
}.uppercase()
println("First item after modifications: '$modifiedFirstItem'")
// 만약 let을 사용하지 않았다면?
val modifiedFirstItem = {
if (numbers.first().length >= 5) numbers.first()
else "!" + numbers.first() + "!"
}
modifiedFirstItem.uppercase()
수신 객체를 블록 내에서 this
로 사용하는 apply
, run
, with
은 중첩되어 사용하지 않습니다. 수신객체의 이름도 변경할 수 없는 함수들이기 때문에 중첩되어 사용할 경우 어떤 수신객체를 사용되는지 확인하기 어렵습니다.
also
와 let
는 중첩해서 사용할 경우 it
을 사용하지 않고 명시적인 이름을 제공하여 이름이 혼동되지 않도록 합니다.