[Android, Kotlin] apply, also, let, with, run의 사용 - 범위 지정 함수
Kotlin

[Android, Kotlin] apply, also, let, with, run의 사용 - 범위 지정 함수

 

Kotlin을 사용 할 때, 범위 지정 함수인 apply, also, let, with, run은 간결한 코드와 가독성을 위해 자주 사용하게 된다.

그러나 이 5가지 범위 지정 함수를 사용 할 때 어떤 차이점이 존재하는지 뚜렷하게 알지 못한 채 사용하게 되는 일이 많다.

 

알지 못하고 쓰면 모두 비슷한 기능을 하는 것 같은 5가지 범위 지정 함수의 차이와 사용 규칙을 확실히 이해하고 올바르게 사용하고자 한다.

 

 

각 함수들을 비교하기 전에, 5가지 차이점을 단박에 이해할 수 있는 좋은 표 하나를 보도록 하자.

 

[호출 시 수신 객체 입력]

* 명시적 전달

with가 수신 객체의 명시적 전달에 해당된다.

with의 Definitions를 보면, 매개변수를 통해 receiver: T를 명시적으로 전달함을 알 수 있다.

 

* 암시적 전달 

apply, also, let, run이 수신 객체의 암시적 전달에 해당된다.

Definitions를 보면, T().apply와 같이 T의 확장함수를 통해 수신 객체를 암시적으로 전달함을 알 수 있다.

 

 

[코드 블록으로 수신 객체 전달]

* 명시적 전달

also, let이 코드 블록(람다)으로 수신 객체 전달의 명시적 전달에 해당된다.

Examples을 보면, 코드 블록 내에 수신 객체가 매개변수로써 명시적으로 전달되어 it을 통해 접근 하는 것을 알 수 있다. 

 

* 암시적 전달

apply, with, run이 코드 블록(람다)으로 수신 객체 전달의 암시적 전달에 해당된다.

Examples을 보면, 코드 블록 내에 수신 객체가 T의 확장함수 형태로 암시적으로 전달되어 this를 통해 접근 하는 것을 알 수 있다.

 

 

[반환]

* 전달 받은 수신 객체

apply, also가 이에 해당된다.

Definitions를 보면, 반환 형태가 T로써 전달 받은 수신 객체를 반환함을 알 수 있다.

 

* 코드 블록의 수행 결과

with, let, run이 이에 해당된다.

Definitions를 보면, 반환 형태가 R로써 코드 블록의 수행 결과에 해당하는 반환 형태를 가짐을 알 수 있다.

 

 

이러한 5가지 범위 지정 함수의 차이점이 존재하기 때문에, 각 함수들은 적재적소에 알맞게 사용해야 한다.

 

 

(1) apply

코드 블록 내에서 수신 객체의 함수를 사용하지 않고 프로퍼티만 사용하여 수신 객체를 반환하는 경우 사용한다.

대표적인 예는 객체의 초기화이다.

 

val user = User().apply {
    name = "HungSeong"
    age = 24
}

 

(2) also

코드 블록 내에서 수신 객체를 사용하지 않거나 수신 객체의 속성을 변경하지 않고 쓸 경우 사용한다.

대표적인 예는 디버깅 시 사이드 이펙트, 값의 확인 등 객체의 유효성 검사이다.

 

val user = user.also {
    requireNotNull(it.name)
    println(it.age)
}

 

(3) let

수신 객체가 null이 아닐 때만 코드를 실행해야 할 경우 사용한다.

또는 지역 변수의 범위를 제한하고자 할 때 사용한다.

 

userInfo()?.let {
    //userInfo가 non-null 일 때만 printUserAge 함수 실행
    printUserAge(it.age)
}


getDatabase().let { db ->
  // 변수 db의 범위가 해당 블록 내로 제한됨
}

 

(4) with

수신 객체가 non-null 이고, 결과를 반환 받을 필요가 없을 경우 사용한다.

 

val user = User("HungSeong", 24)

// user는 non-null이므로 with의 매개변수(수신 객체)로 사용 가능
with(user) { 
    print(user.name)
    print(user.age)
}

 

(5) run

run 함수의 경우, 코드 블록의 마지막 수행 결과를 반환하고 코드 블록 내의 변수는 모두 임시로 사용되는 변수이므로 

코드 블록 내에서 복잡한 계산을 수행 할 때, 여러 지역 변수의 범위를 제한 할 때 사용한다.

 

// 복잡한 계산을 run 코드 블록 내에서 수행
val userStr = run {
  val name = "박훈성"
  val age = 24
  "저는 $name 입니다. 청춘의 나이, $age살 입니다."
}

println(userStr)

 

또한 객체에서 run을 호출하여 코드 블록 내에서 receiver로 수신 객체를 암시적으로 전달하기 위한 용도로도 사용한다.

 

// 수신 객체를 receiver로 코드 블록에 전달
val name = "ParkHunSeong"
name.run {
  toLowerCase() // 암시적 전달을 통해 this 생략 가능
}

 

 

 

각 함수의 차이점 및 사용 용도를 확실하게 이해하여 본 함수의 목적에 맞는 클린 코드를 작성할 수 있도록 노력해야 할 것!

 

 

 

참고 : https://medium.com/@limgyumin/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%9D%98-apply-with-let-also-run-%EC%9D%80-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94%EA%B0%80-4a517292df29

 

코틀린 의 apply, with, let, also, run 은 언제 사용하는가?

원문 : “Kotlin Scoping Functions apply vs. with, let, also, and run”

medium.com

 

https://youngest-programming.tistory.com/578

 

[안드로이드] 코틀린 범위지정함수(Scoping Functions) - let, run, with, apply, also -

[코틀린의 특징] 코틀린은 젯브레인사에서 만든 언어로 JVM에서 동작하고 자바와 100% 호환된다는 특징을 갖고 있습니다. 이밖에도 Null Safety, 함수형프로그래밍, 확장함수, 코드의 간결함 등 다양

youngest-programming.tistory.com