표준 함수

코틀린의 표준함수는 내부적으로 확장 함수이며 확장 함수를 실행하는 주체를 수신자 또는 수신자 객체라고 한다.

apply

val menuFile = File("menu.txt")
menuFile.setReadable(true)
menuFile.setWriteable(true)

// apply 사용
val menuFile = File("menu.txt").apply {
    setReadable(true) // 암시적으로 menuFile.setReadable(true)가 호출된다
    setWriteable(true)
}

let

인자로 전달된 람다를 실행한 후 결과를 반환해 준다.

val firstItemSquared = listOf(1, 2, 3).first().let { it * it }

apply와 let의 차이점

run

val menuFile = File("menu.txt")
val isTest = menuFile.run {
    readText().contains("test")
} 

함수 호출이 여러 개 있을 때 run을 사용하면 편리하고 가독성이 좋아진다.

fun nameIsLong(name: String) = name.length >= 20
fun createMessage(nameTooLong: Boolean): String {
    return if (nameTooLong) {
        "Name is too long. Please choose another name."
    } else {
        "Welcome"
    }
}

// 일반적인 구현
println(createMessage(nameIsLong("my name")))

// run 사용
"my name"
    .run(::nameIsLong)
    .run(::createMessage)
    .run(::println)

With

val nameIsLong = with("my name") {
    length >= 20
}

also

수신자 객체를 반환하기 때문에 서로 다른 처리를 also를 사용해 연쇄 호출할 수 있다.

val fileContents: List<String>
File("file.txt")
    .also { println(it.name) }
    .alse { fileContents = it.readLine() }

takeIf

람다에 제공된 조건식을 실행한 후 그 결과에 따라 true이면 수신자 객체를 false이면 null를 반환한다.

// takeIf를 사용하지 않는 경우
val file = File("file.txt")
val fileContents = if (file.canRead() && file.canWrite()) {
    file.readText()
} else {
    null
}

// takeIf를 사용
val fileContents = File("myfile.txt")
    .takeIf { it.canRead() && it.canWrite() }
    ?.readText()

요약

함수 수신자 객체를 람다의 인자로 전달하는가? 연관 범위를 제공하는가? 반환
let Yes No 람다의 결과
apply No Yes 수신자 객체
run No Yes 람다의 결과
with No Yes 람다의 결과
also Yes No 수신자 객체
takeIf Yes No 수신자 객체의 null 가능 버전

Reference