1. 범위 지정 함수
(1)
val result1 = list.apply {
this.add(1)
add(2) // this 생략 가능
}
(2)
val result2 = list.also {
print("리스트: ${it}") // {} 생략 가능
}
(3)
val result3 = "kotlin"
.let{
it.uppercase()
}
.let{
it.reversed()
}
(4)
val s = "kotlin"
val result4 = with(s) {
println(this)
println(s == this)
uppercase() // this 생략 가능 this.uppercase() or s.uppercase()
}
(5)
val result5 = run {
val a = 10
val b = 20
a + b
} // result5 == 30
(1) apply ( public inline fun <T> T.apply(block: T.() -> Unit): T )
- apply는 객체를 만들고 속성을 설정할 때(초기화) 사용하는 범위 지정 함수로 리시버로 this를 사용하며 리턴 값은 객체 자체이다
inline fun <T> T.apply(block: T.() -> Unit): T {
block() // this.block()
return this
}
(2) also ( public inline fun <T> T.also(block: (T) -> Unit): T )
- also는 객체를 만들고 객체에 대한 부가 작업(로깅, validation 등)을 수행하는 데 사용하는 범위 지정 함수로 리시버로 it을 사용하며 리턴 값은 객체 자체 이다
inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}
(3) let( public inline fun <T, R> T.let(block: (T) -> R): R )
- let은 주로 null체크 후 안전한 실행, 임시 스코프 생성, 체이닝, 값 가공 후 반환 등에 사용할 수 있으며, 리시버로 it을 사용하고 리턴은 마지막 표현식 이다
inline fun <T, R> T.let(block: (T) -> R): R {
return block(this)
}
(4) with ( public inline fun <T, R> with(receiver: T, block: T.() -> R): R )
- with는 확장 함수가 아닌 일반 함수로 객체의 여러 멤버에 접근하거나 조작할 때 사용하며 리시버를 인자로 받고 this를 통해서도 사용할 수 있다
inline fun <T, R> with(receiver: T, block: T.() -> R): R {
return receiver.block()
}
(5) run ( public inline fun <T, R> T.run(block: T.() -> R): R )
- run은 객체를 초기화하거나 계산후 결과를 반환할 때 사용되며, 리시버로 this를 사용하고 마지막 표현식을 반환한다
inline fun <T, R> T.run(block: T.() -> R): R {
return block() // this.block()
}
apply, also, let, run 비교
구현에 있어서는 각각에 대해 차이가 있을 수 있다 실제로 사용해보면 별 차이가 없는 것 처럼 느껴질 수 있다 하지만 이는 함수의 각 역할에 따라 구분을 해둔 것이라고 이해하면 좋을 것 같다
apply는 객체에 대한 속성을 설정하는 역할, also는 생성된 객체에 대한 validation이나 logging등 부가적인 작업, let은 안전한 null처리나 임시 스코프 생성 등의 역할, run은 초기화 및 계산후 결과 반환 등의 역할을 수행한다
다만, 그렇다면 가령 (2)에 대해서는 변경을 제한하는 등의 작업이 있으면 좋을 텐데 그렇지 않은 것은 실제 사용하는 환경에서는 이러한 역할을 딱딱 맞게 나누기 어렵기도 하기 때문에 역할 구분해두고 이렇게 쓰기를 권장하는 방향으로 기능이 존재한다