개념
- 코틀린의 표준 라이브러리에서 함수형 프로그래밍을 쉽게 적용할 수 있도록 도와주는 함수 키워드
- 종류
- apply, with, let, also, run
- 수신 객체(receiver) + 수신 객체 지정 람다(lamda with receiver) 로 구성되어 있다.
- 여기서 수신 객체는 scope function 키워드를 적용하는 기준이 되는 객체를 의미한다.
- 수신 객체를 기준으로 확장 함수 형태로 사용하거나 수신 객체를 파라미터로 받는 형식으로 사용한다.
- scope function 키워드들은 얼핏 매우 비슷한 기능을 하는 것 처럼 보이기 때문에 상황에 맞는 적절한 scope function 을 선택해서 사용하는 것이 중요하다.
With
정의 및 특징
inline fun <T, R> with(receiver: T, block: T.() -> R): R {
return receiver.block()
}
- 수신 객체의 확장 함수 형태로 수신 객체 지정 람다를 정의한다.
- 람다 블록 안에서
this
키워드를 사용하여 수신 객체를 호출하고 다룬다.
- 수신객체의 프로퍼티를 사용할 때는
this
를 생략할 수 있다.
- 람다 블록 가장 마지막에 원하는 것을 반환한다.
사용하는 경우
- 수신 객체의 프로퍼티나 메서드를 활용하여 반환할 무언가 만들어야하는 상황에서 사용하기 좋다.
- 보통 non-null 타입의 수신 객체를 다룰 때 사용한다.
- 반환 결과를 사용하지 않는 경우도 많다.
예시
class Note(var content: String, var paperSize: Int)
fun main() {
val note = Note("abc", 3)
val notePrice = with(note) {
println("내용은 ${this.content} 이고 종이 사이즈는 $paperSize 입니다.")
(content.length + paperSize) * 100
}
println(notePrice)
}
also
정의 및 특징
inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}
- 수신 객체를 사용하여 반환하는 것이 없는 작업을 하고 다시 수신 객체를 반환한다.
- 람다 블록에서는
it
으로 수신 객체를 호출한다.
사용하는 경우
- 일반적으로 객체 생성과 동시에 해당 객체를 이용해서 부가적으로 처리해줘야 하는게 있는 경우 사용하면 가독성이 좋다.
- 수신 객체의 사이드 이펙트를 확인 할 때
- 수신 객체의 생성과 동시에 프로퍼티에 대한 유효성 검사를 할 때
예시
class Note(var content: String, var paperSize: Int)
fun main() {
val note = Note("abc", 3).also {
println("content = ${it.content} | paperSize = ${it.paperSize}")
}
}
apply
정의 및 특징
inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
- 수신 객체에 대한 반환하는 것이 없는 작업을 한 뒤 수신 객체를 다시 반환한다는 점에서
also
와 굉장히 흡사하다.
- 차이점은 람다 블록이 수신객체의 확장함수를 정의하는 방식이기 때문에 블록 내부에서
it
이 아닌 this
로 수신 객체를 다룬다.
- 수신객체의 프로퍼티를 사용할 때는
this
를 생략할 수 있다.
사용하는 경우
- 사실상 also 와 혼용해서 사용해도 될 정도이긴하나 코틀린에서 선호하는 모범 사례가 있긴하다.
- 수신 객체의 프로퍼티 만을 사용하는 경우에 주로 사용한다.
- 수신 객체의 프로퍼티 초기화를 할 때
- 수신 객체의 프로퍼티에 대한 작업을 해야할 때
예시
class Note() {
var content: String? = null
var paperSize: Int? = null
}
fun main() {
val note = Note().apply {
this.content = "abc"
paperSize = 3
}
}
let
정의 및 특징
inline fun <T, R> T.let(block: (T) -> R): R {
return block(this)
}
- 가장 단순한 형태, 일반적인 형태라고 할 수 있는 scope function
- 람다 블록이 수신 객체를 매개변수로 받아서 작업을 한 뒤 무언가를 반환한다.
- 람다 블록에서는
it
으로 수신 객체를 호출한다.
사용하는 경우
- 매우 다양한 상황에서 사용할 수 있는 scope function 이지만 코틀린에서 선호는 모범 사례가 존재한다.
- 주로 nullable 한 수신 객체를 다룰 때 사용한다.
?.
연산자와 함께 사용해서 수신 객체가 null
이 아닌 경우에만 특정 람다 블록이 동작하도록 하고 싶을 때
- nullable 한 객체를 다른 nullable 한 객체로 변환하는 경우
- 특정 지역 변수의 사용 범위를 제한하는 경우.
let
의 람다 블록 안에서만 사용하는 것을 명시하고 싶은 경우이다.
예시
val note: Note? = getNullableNote()?.let {
send(it)
}
run
정의 및 특징
inline fun <T, R> T.run(block: T.() -> R): R {
return block()
}
사용하는 경우
- 주로 수신 객체 없는 형태로 많이 사용한다.
- 수신 객체가 없은 형태로 사용하면서 지역 변수들의 범위를 제한해서 사용하고 싶을 때 주로 사용한다.
- 특정 지역 변수와 로직을 run 의 람다 블록으로 묶어서 작업하고 그것을 명시하고 싶을 때