코틀린

코틀린 apply, with, let, also, run

태인킴 2020. 12. 8. 23:05
반응형


코틀린의 apply, with, let, also, run과 같은 함수들을 '범위 지정 함수'라고 합니다. 각각의 쓰임새에 대해서 알아 보겠습니다.

 

 

1. 정의

먼저, apply, with, let, also, run의 정의를 확인 하겠습니다.

// with() 정의
inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block()
}

// also() 정의
inline fun <T> T.also(block: (T) -> Unit): T {
    block(this)
    return this
}

// apply() 정의
inline fun <T> T.apply(block: T.() -> Unit): T {
    block()
    return this
}

// let() 정의
inline fun <T, R> T.let(block: (T) -> R): R {
    return block(this)
}

// run() 정의
inline fun <T, R> T.run(block: T.() -> R): R {
    return block()
}

 

 

2. 간단한 사용 예시

run

val r: R = T( ).run { this.foo();  this.toR()  }

with

val r: R = with(T( )) { this.foo();  this.toR()  }

apply

val t: T = T( ).apply { this.foo() }

also

val t: T = T( ).also { it.foo() }

let

val r: R = T( ).let { it.foo();  it.toR()  }

 

 

3. apply 사용 규칙

수신 객체 람다 내부에서 수신 객체의 함수사용하지 않고, 자신다시 반환 하려는 경우에 apply 를 사용합니다. 수신 객체의 프로퍼티만을 사용하여 객체 초기화를 하는 경우가 대표적인 예 입니다.

// apply()를 사용하지 않는 경우
val killer = Person()
killer.name = "Killer"
killer.age = 21


// apply()를 사용하는 경우
val killer = Person().apply {
    // apply의 블록 에서는 오직 프로퍼티만 사용합니다!
    name = "Killer"
    age = 21
}

 

 

4. also 사용 규칙

람다 내부에서 수신 객체전혀 사용 하지 않거나, 수신 객체의 속성변경하지 않고 사용하는 경우 also 를 사용합니다. alsoapply와 마찬가지로 수신 객체반환 합니다. 예를 들어, 객체의 사이드 이팩트를 확인하거나, 데이터를 할당하기 전에 해당 데이터의 유효성을 검사 할 때 매우 유용합니다.

//also()를 사용하지 않는 경우
class Book(val author: Person) {
    init {
      requireNotNull(author.age)
      print(author.name)
    }
}


//also()를 사용하는 경우
class Book(author: Person) {
    val author = author.also {
      requireNotNull(it.age)
      print(it.name)
    }
}

 

 

5. let 사용 규칙

  • 지정된 값이 null 이 아니면서, 코드를 실행해야 하는 경우
  • Nullable객체 다른 Nullable객체로 변환하는 경우
  • 해당 객체를 람다 내부 지역 변수로 범위를 제한 하는 경우
//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)


//let()을 사용하는 경우
getNullablePerson()?.let {
    // null 이 아닐때만 실행됩니다.
    promote(it)
}
val driversLicence: Licence? = getNullablePerson()?.let {
    // nullable personal객체를 nullable driversLicence 객체로 변경합니다.
    licenceService.getDriversLicence(it) 
}
val person: Person = getPerson()
getPersonDao().let { dao -> 
    // 변수 dao 의 범위는 이 블록 안 으로 제한 됩니다.
    dao.insert(person)
}

 

 

6. with 사용 규칙

수신 객체가 Non-Nullable 이고, 반환값이 필요하지 않은 경우 with를 사용합니다.

//with()를 사용하지 않는 경우
val person: Person = getPerson()
print(person.name)
print(person.age)


//with()를 사용하는 경우
val person: Person = getPerson()
with(person) {
    print(name)
    print(age)
}

 

 

7. run 사용 규칙

  • 어떤 값을 계산할 필요가 있거나, 여러개지역 변수의 범위제한하는 경우.
  • 매개 변수로 전달된 명시적 수신객체암시적 수신 객체변환 하는 경우.
//run()을 사용하지 않는 경우
val person: Person = getPerson()
val personDao: PersonDao = getPersonDao()
val inserted: Boolean = personDao.insert(person)
fun printAge(person: Person) = {
    print(person.age)
}


//run()을 사용하는 경우
val inserted: Boolean = run {
    // person 과 personDao 의 범위를 제한 합니다.
    val person: Person = getPerson()
    val personDao: PersonDao = getPersonDao()
    // 수행 결과를 반환 합니다.
    personDao.insert(person)
}
fun printAge(person: Person) = person.run {
    // person 을 수신객체로 변환하여 age 값을 사용합니다.
    print(age)
}

 

 

8. 5개의 범위 지정 함수 결합

범위 지정 함수중첩되면 코드의 가독성이 떨어집니다. 따라서, 중첩을 피하는 것이 좋습니다.

하지만, 체인 형식으로 적절히 조합 하여 사용하면, 코드의 가독성이 향상 됩니다. 다음은 체인 형식으로 잘 사용된 예 입니다.

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
}
  • SQL 준비 - apply
  • SQL 로그 출력 - also
  • SQL 실행 및 boolean 반환 - run

위와 같이, 범위 지정 함수를 체인 형식으로 적절히 분리 하여 사용 할수 있습니다.

반응형

'코틀린' 카테고리의 다른 글

코틀린 setter, getter  (0) 2021.02.20
Kotlin Unit Test 하는법  (0) 2020.12.10
Kotlin 코루틴(Coroutine)  (0) 2020.12.07
코틀린 SAM 변환이 왜 되지 않을까?  (0) 2020.11.25
코틀린 6편 (lateinit)  (0) 2020.11.13