Kotlin

Scope 함수(범위 지정 함수 - apply, with, let, also, run)

kakaroo 2022. 3. 26. 01:26
반응형

코틀린의 표준 라이브러리는 다양한 기능 과 편리한 기능으로 함수형 프로그래밍을 쉽게 적용할 수 있도록 도와줍니다.

그중에서도 apply, with, let, also, run 과 같은 범위 지정함수에 대해 알아보겠습니다.

이 5개의 함수는 전달받는 인자와 작동 방식, 결과가 매우 비슷하기 때문에 많은 경우에 서로를 대체 해서 사용할수도 있습니다.

 

이 함수들은 두가지 구성 요소를 가집니다.

  • 수신 객체 (확장함수가 호출되는 대상이 되는 객체)
  • 수신 객체 지정 람다 (lambda with receiver)

 

 

apply 사용 규칙

수신 객체 람다 내부에서 수신 객체의 함수를 사용하지 않고 수신 객체 자신을 다시 반환 하려는 경우에 apply 를 사용합니다.

수신 객체 의 프로퍼티 만을 사용하는 대표적인 경우가 객체의 초기화 이며, 이곳에 apply 를 사용합니다.

 

inline fun <T> T.apply(block: T.() -> Unit): T
//Calls the specified function block with this value as its receiver and returns this value.

val peter = Person().apply {
    // apply 의 블록 에서는 오직 프로퍼티 만 사용합니다!
    name = "Peter"
    age = 18
}

//apply를 사용하지 않은 코드
val peter = Person()
peter.name = "Peter"
peter.age = 18

 

also 사용 규칙

수신 객체 람다가 전달된 수신 객체를 전혀 사용 하지 않거나 수신 객체의 속성을 변경하지 않고 사용하는 경우 also 를 사용합니다.

also 는 apply 와 마찬가지로 수신 객체를 반환 하므로 블록 함수가 다른 값을 반환 해야하는 경우에는 also 를 사용할수 없습니다.

예를 들자면, 객체의 사이드 이팩트를 확인하거나 수신 객체의 프로퍼티에 데이터를 할당하기 전에 해당 데이터의 유효성을 검사 할 때 매우 유용합니다.

 

inline fun <T> T.also(block: (T) -> Unit): T
//Calls the specified function block with this value as its argument and returns this value.

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)
    }
}

 

with 사용 규칙

Non-nullable (Null 이 될수 없는) 수신 객체 이고 결과가 필요하지 않은 경우에만 with 를 사용합니다.

수신객체에 대한 작업 후 마지막 라인을 리턴합니다. run과 완전히 똑같지만, run은 확장 함수로 사용되지만, with는 수신 객체를 파라미터로 받아 사용한다는 점이 다릅니다.

 

inline fun <T, R> with(receiver: T, block: T.() -> R): R
//Calls the specified function block with the given receiver as its receiver and returns its result.

class Person {
    var name: String? = null
    var age: Int? = null
}

val person: Person = getPerson()
with(person) {
    print(name)
    print(age)
}

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

 

let 사용 규칙

다음과 같은 경우에 let 을 사용합니다.

  • 지정된 값이 null 이 아닌 경우에 코드를 실행해야 하는 경우.
  • Nullable 객체를 다른 Nullable 객체로 변환하는 경우.
  • 단일 지역 변수의 범위를 제한 하는 경우.

inline fun <T, R> T.let(block: (T) -> R): R
//Calls the specified function block with this value as its argument and returns its result.

getPromotablePerson()?.let {
    // null 이 아닐때만 실행됩니다.
    promote(it)
}

//let을 사용하지 않는 경우
val person: Person? = getPromotablePerson()
if (person != null) {
  promote(person)
}

 

연산 결과를 임시 변수에 할당하지 않고 처리하는 경우

val num = mutableListOf(1, 5, 14, 2, 7, 16, 24, 8)
val result = num.filter{it > 10}
println(result) //[14, 16, 24]

//result 변수 없이 바로 사용 가능
num.filter{it > 10}.let{println(it)}    //[14, 16, 24]

 

코드 블럭에 it을 인자로 가지는 단일 함수가 있다면 람다 대신 메서드 참조(::)를 사용할 수도 있습니다.

.let{println(it)}	--> .let(::println)

 

run 사용 규칙

어떤 값을 계산할 필요가 있거나 여러개의 지역 변수의 범위를 제한하려면 run 을 사용합니다.

매개 변수로 전달된 명시적 수신객체 를 암시적 수신 객체로 변환 할때 run ()을 사용할수 있습니다.

 

수신객체를 리턴하지 않고, run 블럭의 마지막 라인을 리턴합니다.

수신객체에 대해 특정한 동작을 수행한 후 결과값을 리턴받고자 할 때 주로 사용됩니다.

 

inline fun <T, R> T.run(block: T.() -> R): R
//Calls the specified function block with this value as its argument and returns its result.

 

data class Person (var name: String = "", var age: Int = 0) {
	fun isOld(): Boolean = age > 50
}

val person = Person("james", 55)
val isOldAge = person.run {
    age = 40
    isOld()	//return 값
}
반응형

'Kotlin' 카테고리의 다른 글

reduce, fold  (0) 2022.03.26
lateinit, lazy  (0) 2022.03.26
backing field <custom getter(), setter() 설정>  (0) 2022.03.26
val, const val  (0) 2022.03.25
람다, 고차함수, 함수합성, 클로저  (0) 2022.03.25