Kotlin 표준(Scope)함수: apply, also, with, run, let
Language/Kotlin

Kotlin 표준(Scope)함수: apply, also, with, run, let

728x90
반응형

Scope Function 이라는 함수명에서 알 수 있듯이,
이 함수들을 람다식을 이용해서 호출하면 일시적인 Scope(범위)가 생기게 되고,
이 범위 안에서는 전달된 객체에 대해 "it" 또는 "this" 라는 Context Object를 통해서 접근하게 됩니다.

 


Scope Function에는 서로 다른 두 가지 주요 차이점이 있습니다.
1) Context Object를 참조하는 방법 (this, it)
2) Return value


1) Context Object: this or it

Scope Function 람다식 내에서 Context Object는 실제 객체명 대신, "it" 또는 "this" 키워드로 접근하게 됩니다.


this

  • run, with, apply 는 Context Object를 "this" 로 참조합니다.
  • 따라서, 람다식 안에서는 일반 클래스 멤버처럼 사용할 수 있습니다.
  • this는 생략할 수 있지만, 만약 동일한 이름의 멤버가 있을 경우 구별할 수가 없기 때문에, 가급적이면 Context Object에 대해서는 this를 붙여서 사용하는 것이 좋습니다.

it

  • let, also 는 Context Object를 "it" 로 참조합니다.
  • 따로 전달 인자명을 지정할 수도 있고, 지정하지 않으면 기본적으로는 "it" 로 접근하게 됩니다.

 

2) Return Value


- apply, also는 Context Object를 반환
- let, run, with는 람다식 결과를 반환

 


Context Object

  • apply, also의 반환 값은 Context Object 객체 자체입니다.
  • 그렇기 때문에, 체인 형식으로 계속적인 호출이 가능하고,
  • Context Object를 반환하는 함수의 return문에도 사용할 수 있습니다.

 

Lambda Result

  • let, run, with는 람다식 결과를 반환합니다.
  • 그렇기 때문에 결과를 변수에 할당하거나, 결과에 대해 추가적인 작업 등을 수행할 때 사용할 수 있습니다.
  • 또한, 반환 값을 무시하고 바로 람다식을 사용하여, 임시 범위를 만들어서 사용할 수도 있습니다.

 

 

출처: https://0391kjy.tistory.com/25

 

 

이제부터 각 Scope 함수들에 대한 설명 및

권장하는 사용 케이스과 사용스 타일에 대하여 살펴보도록 하겠습니다. 

 


apply


  • Context Object : this
  • Return Value : context object

 

apply는 보통 객체 초기화 시 가장 많이 사용됩니다.

val person = Person("홍길동").apply {
                  age = 30
             }

 

객체를 생성하면서 함께 호출해야 하는 초기화 코드가 있는 경우 사용

val param = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT)
param.gravity = Gravity.CENTER_HORIZONTAL
param.weight = 1f
param.topMargin = 100
param.bottomMargin = 100
val param = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
    gravity = Gravity.CENTER_HORIZONTAL
    weight = 1f
    topMargin = 100
    bottomMargin = 100
}

 


also


  • Context Object : it
  • Return Value : context object

also는 기존 객체를 수정하거나 변경하지 않고,

디버깅을 위한 로깅 등의 추가적인 부가 작업을 하려고 할 때 사용합니다.

val numbers = mutableListOf("one", "two", "three")
numbers
    .also { println("새 항목 추가하기 전 리스트 요소들: $it") }
    .add("four")

 

 

 


let


함수를 호출하는 객체를 이어지는 블록의 인자로 넘기고, 블록의 결과값을 반환합니다.

  • Context Object : it
  • Return Value : lambda result

 

1) 객체 결과값에 하나 이상의 함수를 호출하는 경우 사용합니다.

/** let 안썼을 때 */
val numbers = mutableListOf("one", "two", "three", "four", "five")
val resultList = numbers.map { it.length }.filter { it > 3 }
println(resultList)    

/** let 사용 시 */
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map { it.length }.filter { it > 3 }.let { 
    println(it)
    // 추가적인 함수 호출 가능
} 



2) 또한, let은 null이 아닌 값으로만 코드 블록을 실행시키고 싶을 때 자주 사용됩니다.

null이 아닌 객체에 대해 작업을 수행하려면 안전한 호출 연산자(?.) 를 let에 사용하도록 합니다.

val str: String? = "Hello"   
val length = str?.let { //안전한 호출 Safe Call
    println("let() 호출 $it")        
}

let()을 안전한 호출(?.)과 함께 사용하면 if (null != obj) ... 를 대체할 수 있다. 

if (null != obj) {
	/// 수행할 코드
}
obj?.let {
	/// 수행할 코드
}
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {

    // imgUrl이 null 이 아닐경우 수행 >>>>>  let() + ?.
    imgUrl?.let {
        val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
        Glide.with(imgView.context)
            .load(imgUri)
            .apply(
                RequestOptions()
                .placeholder(R.drawable.loading_animation)  
                .error(R.drawable.ic_broken_image))      
            .into(imgView)
    }
}

 


with


  • Context Object : this
  • Return Value : lambda result

run 과 거의 동일하지만 안전한 호출(Safe Calls)을 지원하지 않음

with는 이미 생성된 Context Object 객체를 인자로 받아서 사용하는 것이 효율적일 때는 with를 사용하면 더 좋습니다

val numbers = mutableListOf("one", "two", "three")
with(numbers) {
    println("'with' 는 ${this} 로 참조합니다.")
    println("${size}개의 요소를 포함합니다.")
}
val numbers = mutableListOf("one", "two", "three")
val firstAndLast = with(numbers) {
    "첫번째 요소는 ${first() 입니다.}," +
    "마지막 요소는 ${last() 입니다.}"
}
println(firstAndLast)

 

 


run


  • Context Object : this
  • Return Value : lambda result

with와 비슷한 역할로, 이미 생성된 Context Object 객체를 사용할 때 호출하며, with와는 전달받는 위치가 다릅니다.
그리고, 가장 중요한 차이점은 앞에 Safe Call (?.)을 붙여서 null 체크까지 할 수 있기 때문에, with보다는 run이 더 자주 사용되는 이유 중 하나라고 할 수 있습니다.

/** 기본 코드 */
val point = Point()
val width = point.x * 0.5

/** run() 사용 코드 */
val width = run {
    val point = Point()
    point.x * 0.5
}

val point = Point()
val width = point.run {
    x * 0.5
}

imageView.layoutParams.run {
    width = 400
    height = 200
}
 supportActionBar?.run { // 이미 생성된 객체인 supportActionBar 가 널이 아닐경우에만 수행
        setDisplayHomeAsUpEnabled(true)
        setHomeAsUpIndicator(R.drawable.ic_clear_white)
    }

 

 

 

참고

https://0391kjy.tistory.com/25

https://androidhuman.com/2016-07-06-kotlin_let_apply_run_with

728x90
반응형