Kotlin Scoping Functions apply vs. with, let, also, and run
코틀린에서는 5가지의 확장함수를 제공합니다.
각각의 정의의 공통점과 차이점을 살펴본 후 언제, 어떻게 사용할지 알아봅시다.
일단 각 함수의 정의는 다음과 같다.
inline fun <T, R> with(receiver: T, block: T.() -> R): R {
return receiver.block()
}
inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}
inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
inline fun <T, R> T.let(block: (T) -> R): R {
return block(this)
}
inline fun <T, R> T.run(block: T.() -> R): R {
return block()
}
정의의 차이점을 살펴보자면
1. 명시적인 파라메터로 전달되는 Receiver vs 암시적인 Reciever
람다식에 Receiver가 명시적으로 전달 vs 암식적으로 전달
Receiver를 리턴 vs 람다식의 결과를 리턴
친절하게도 표로 정리도 되어있다.
val peter = Person().apply {
// only access properties in apply block!
name = "Peter"
age = 18
}
만일 apply()를 사용하지 않는다면 다음과 같습니다.
val clark = Person()
clark.name = "Clark"
clark.age = 18
class Book(author: Person) {
val author = author.also {
requireNotNull(it.age)
print(it.name)
}
}
만일 also()를 사용하지 않는다면 다음과 같습니다.
class Book(val author: Person) {
init {
requireNotNull(author.age)
print(author.name)
}
}
getNullablePerson()?.let {
// null이 아닐때만 실행된다.
promote(it)
}
val driversLicence: Licence? = getNullablePerson()?.let {
// getNullablePerson을 nullable한 driversLicence로 변환
licenceService.getDriversLicence(it)
}
val person: Person = getPerson()
getPersonDao().let { dao ->
// 변수 dao는 이 블록안으로 제한
dao.insert(person)
}
만일 let()를 사용하지 않는다면 다음과 같습니다.
val person: Person? = getPromotablePerson()
if (person != null) {
promote(person)
}
val driver: Person? = getDriver()
val driversLicence: Licence? = if (driver == null) null else
licenceService.getDriversLicence(it)
val person: Person = getPerson()
val personDao: PersonDao = getPersonDao()
personDao.insert(person)
val person: Person = getPerson()
with(person) {
print(name)
print(age)
}
만일 with()를 사용하지 않는다면 다음과 같습니다.
val person: Person = getPerson()
print(person.name)
print(person.age)
val inserted: Boolean = run {
val person: Person = getPerson()
val personDao: PersonDao = getPersonDao()
personDao.insert(person)
}
fun printAge(person: Person) = person.run {
print(age)
}
만일 run()를 사용하지 않는다면 다음과 같습니다.
val person: Person = getPerson()
val personDao: PersonDao = getPersonDao()
val inserted: Boolean = personDao.insert(person)
fun printAge(person: Person) = {
print(person.age)
}
// SQL 준비, SQL 로깅, SQL 실행, 그리고 insert가 성공여를
//나타내는 Boolean값을 리턴한다.
private fun insert(user: User) = SqlBuilder().apply {
append("INSERT INTO user (email, name, age) VALUES ")
append("(?", user.email)
append(",?", user.name)
append(",?)", user.age)
}.also {
print("Executing SQL update: $it.")
}.run {
jdbc.update(this) > 0
}