[Android] Kotlin – apply/with/let/also/run
.
코틀린에서 자주 사용되는 inline 함수 apply, with, let, also, run 의 차이점은 아래와 같습니다.
input – receiver/parameter 는 5종의 inline 함수가 호출될 때 참조할 객체와 어떤 관계를 가지고 호출되는지를 나타냅니다.
- receiver 타입인 apply, run, also, let 은 해당 객체의 확장 함수 형태로 호출됩니다.
- parameter 타입인 with 는 참조할 객체를 파라미터로 넘깁니다. 그리고 파라미터로 넘긴 객체의 확장 함수 형태로 코드블록이 실행됩니다.
apply 함수와 with 함수의 정의와 용례를 보면 어떤 내용인지 알 수 있습니다.
// apply 정의 inline fun <T> T.apply(block: T.() -> Unit): T { block() return this } class Person { var name: String? = null var age: Int? = null } val person = Person().apply { name = "Peter" age = 18 }
apply 함수는 Person 객체의 확장 함수처럼 실행되므로 name, age 값을 직접 변경합니다. 코드블록 안에서 this 를 이용해서 person 객체를 참조할 수 있습니다.
반면 with 에서는 파라미터로 넘긴 person 객체의 확장 함수처럼 실행됩니다. 이 경우도 코드블록 안에서 this 를 이용해서 person 객체를 참조할 수 있습니다.
// with 정의 inline fun <T, R> with(receiver: T, block: T.() -> R): R { return receiver.block() } val person: Person = Person() with(person) { name = "Peter" age = 18 }
binding in lambda – receiver/parameter 는 inline 함수가 정의한 코드블록 안에서 객체를 어떻게 참조할 수 있는지를 나타냅니다.
- receiver 타입인 apply, run 은 해당 객체의 확장 함수처럼 실행됩니다. 그리고 this 를 이용해 객체를 참고할 수 있습니다.
- parameter 타입인 also, let 은 해당 객체를 parameter 로 받습니다.
run, let 함수의 정의와 용례를 보겠습니다.
inline fun <T, R> T.run(block: T.() -> R): R { return block() } fun start() = person.run { print(name) print(age) }
run 의 정의를 보면 T.run(block: T.() -> R): R 코드블록이 객체의 확장 함수처럼 실행됨을 알 수 있습니다. 따라서 객체를 this 로 참조할 수 있습니다. 대신 참조의 범위가 객체 내부로 한정됩니다.
let 의 경우
inline fun <T, R> T.let(block: (T) -> R): R { return block(this) } fun print() { val address: String = "Korea" person?.let { print(it.name) print(address) } }
let 의 정의를 보면 T.let(block: (T) -> R): R 코드블록이 객체를 파라미터로 받음을 알 수 있습니다. 따라서 코드블록 내에서 it 으로 참조할 수 있습니다. 대신 참조의 범위가 let 이 실행되는 코드를 포함한 블록이 됩니다.(예제에서는 print() 함수)
output – same object, result of lambda 는 인라인 함수가 무엇을 리턴하는지 알려줍니다.
- same object 는 참조한 객체를 리턴합니다.
- result of lambda 는 코드블록에 명시한 실행 결과를 리턴합니다.
also 함수의 정의와 예제를 보겠습니다. also 함수는 input – receiver 타입이며 binding in lambda – parameter 타입이고, output – same object입니다.
따라서 참조할 객체의 확장 함수처럼 사용하며, 참조할 객체를 파라미터로 받습니다. 그리고 참조한 객체를 리턴합니다.
inline fun <T> T.also(block: (T) -> Unit): T { block(this) return this } class Person { var name: String? = null var age: Int? = null fun printName() { print(name) } } fun sample() { person.also { print(it.age) }.printName() }
let 함수의 경우 다른 특성은 also 와 같지만 output – result of lambda 이므로 코드블록 실행 결과를 리턴합니다.
fun sample() { var myName: String? = person.let { it.name } }
.
.
정리
- 참조할 객체에 binding 하는 속성을 판단
- with 의 경우만 parameter 형태로 객체를 전달, 나머지는 객체의 확장 함수처럼 사용
- 객체가 코드 블록 내부에서 어떻게 참조되는가
- apply, run, with 의 경우 receiver 형태로 실행되므로 this 로 참조
- also, let 의 경우는 객체를 파라미터로 받으므로 it 으로 참조
- receiver/parameter 의 차이에 따라 코드 블록이 참조할 수 있는 범위 제한이 바뀜
- 코드 블록 실행 후 무엇을 리턴하는가
- apply, also 는 참조한 객체
- run, let 은 코드 블록의 실행 결과
.
참고
- https://medium.com/@limgyumin/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%9D%98-apply-with-let-also-run-%EC%9D%80-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80-4a517292df29
- https://medium.com/@fatihcoskun/kotlin-scoping-functions-apply-vs-with-let-also-run-816e4efb75f5
.
.