Kotlin Scope-function 알고 쓰기

코틀린 표준 라이브러리로 확장함수(extension function)들을 제공합니다.
확장함수에는 범위지정함수(scope-functions) 인 apply, also, with, run, let 이 있고, 언제 사용하면 좋을지 & 저는 어떻게 사용하는지 공유해봅니다.


apply

블록 내에서 수신객체를 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)

also

블록 내에서 수신객체를 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")

with

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"

run

블록 내에서 수신객체를 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

let

블록 내에서 수신객체를 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은 중첩되어 사용하지 않습니다. 수신객체의 이름도 변경할 수 없는 함수들이기 때문에 중첩되어 사용할 경우 어떤 수신객체를 사용되는지 확인하기 어렵습니다.

alsolet 는 중첩해서 사용할 경우 it을 사용하지 않고 명시적인 이름을 제공하여 이름이 혼동되지 않도록 합니다.

참조

좋은 웹페이지 즐겨찾기