개념


  • 코틀린의 표준 라이브러리에서 함수형 프로그래밍을 쉽게 적용할 수 있도록 도와주는 함수 키워드
    • 종류
      • apply, with, let, also, run
  • 수신 객체(receiver) + 수신 객체 지정 람다(lamda with receiver) 로 구성되어 있다.
    • 여기서 수신 객체는 scope function 키워드를 적용하는 기준이 되는 객체를 의미한다.
  • 수신 객체를 기준으로 확장 함수 형태로 사용하거나 수신 객체를 파라미터로 받는 형식으로 사용한다.
  • scope function 키워드들은 얼핏 매우 비슷한 기능을 하는 것 처럼 보이기 때문에 상황에 맞는 적절한 scope function 을 선택해서 사용하는 것이 중요하다.

With


정의 및 특징

inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block()
}
  • 수신 객체의 확장 함수 형태로 수신 객체 지정 람다를 정의한다.
  • 람다 블록 안에서 this 키워드를 사용하여 수신 객체를 호출하고 다룬다.
  • 수신객체의 프로퍼티를 사용할 때는 this 를 생략할 수 있다.
  • 람다 블록 가장 마지막에 원하는 것을 반환한다.

사용하는 경우

  • 수신 객체의 프로퍼티나 메서드를 활용하여 반환할 무언가 만들어야하는 상황에서 사용하기 좋다.
  • 보통 non-null 타입의 수신 객체를 다룰 때 사용한다.
  • 반환 결과를 사용하지 않는 경우도 많다.

예시

class Note(var content: String, var paperSize: Int)

fun main() {
    val note = Note("abc", 3)
    
    val notePrice = with(note) {
        println("내용은 ${this.content} 이고 종이 사이즈는 $paperSize 입니다.")
        (content.length + paperSize) * 100
    }
    println(notePrice)

    // < 출력 결과 >
    // 내용은 abc 이고 종이 사이즈는 3 입니다.
    // 600
}

also


정의 및 특징

inline fun <T> T.also(block: (T) -> Unit): T {
    block(this)
    return this
}
  • 수신 객체를 사용하여 반환하는 것이 없는 작업을 하고 다시 수신 객체를 반환한다.
    • 람다 블록에서는 it 으로 수신 객체를 호출한다.

사용하는 경우

  • 일반적으로 객체 생성과 동시에 해당 객체를 이용해서 부가적으로 처리해줘야 하는게 있는 경우 사용하면 가독성이 좋다.
    • 수신 객체의 사이드 이펙트를 확인 할 때
    • 수신 객체의 생성과 동시에 프로퍼티에 대한 유효성 검사를 할 때

예시

class Note(var content: String, var paperSize: Int)

fun main() {
    val note = Note("abc", 3).also {
        println("content = ${it.content} | paperSize = ${it.paperSize}")
    }
    
    // <출력 결과 >
    // content = abc | paperSize = 3
}

apply


정의 및 특징

inline fun <T> T.apply(block: T.() -> Unit): T {
    block()
    return this
}
  • 수신 객체에 대한 반환하는 것이 없는 작업을 한 뒤 수신 객체를 다시 반환한다는 점에서 also 와 굉장히 흡사하다.
  • 차이점은 람다 블록이 수신객체의 확장함수를 정의하는 방식이기 때문에 블록 내부에서 it 이 아닌 this 로 수신 객체를 다룬다.
  • 수신객체의 프로퍼티를 사용할 때는 this 를 생략할 수 있다.

사용하는 경우

  • 사실상 also 와 혼용해서 사용해도 될 정도이긴하나 코틀린에서 선호하는 모범 사례가 있긴하다.
  • 수신 객체의 프로퍼티 만을 사용하는 경우에 주로 사용한다.
    • 수신 객체의 프로퍼티 초기화를 할 때
    • 수신 객체의 프로퍼티에 대한 작업을 해야할 때

예시

class Note() {
    var content: String? = null
    var paperSize: Int? = null
}

fun main() {
	// Note 객체 초기화
    val note = Note().apply {
        this.content = "abc"
        paperSize = 3
    }
}

let


정의 및 특징

inline fun <T, R> T.let(block: (T) -> R): R {
    return block(this)
}
  • 가장 단순한 형태, 일반적인 형태라고 할 수 있는 scope function
  • 람다 블록이 수신 객체를 매개변수로 받아서 작업을 한 뒤 무언가를 반환한다.
    • 람다 블록에서는 it 으로 수신 객체를 호출한다.

사용하는 경우

  • 매우 다양한 상황에서 사용할 수 있는 scope function 이지만 코틀린에서 선호는 모범 사례가 존재한다.
  • 주로 nullable 한 수신 객체를 다룰 때 사용한다.
    • ?. 연산자와 함께 사용해서 수신 객체가 null 이 아닌 경우에만 특정 람다 블록이 동작하도록 하고 싶을 때
    • nullable 한 객체를 다른 nullable 한 객체로 변환하는 경우
    • 특정 지역 변수의 사용 범위를 제한하는 경우.
      • let 의 람다 블록 안에서만 사용하는 것을 명시하고 싶은 경우이다.

예시

val note: Note? = getNullableNote()?.let {
    // null 이 아닐 때만 실행된다.
    send(it)
}

run


정의 및 특징

inline fun <T, R> T.run(block: T.() -> R): R {
    return block()
}
  • let 과 매우 유사한 형태의 scope function
  • let 과 다른점은 람다 블록 안에서 this 를 사용해서 수신 객체를 호출한다.
  • 수신객체의 프로퍼티를 사용할 때는 this 를 생략할 수 있다.
  • scope function run 은 수신 객체가 없는 형태도 존재해서 수신 객체 없이 사용할 수 있다.
    inline fun <R> run(block: () -> R): R {
    		return block()
    }

사용하는 경우

  • 주로 수신 객체 없는 형태로 많이 사용한다.
  • 수신 객체가 없은 형태로 사용하면서 지역 변수들의 범위를 제한해서 사용하고 싶을 때 주로 사용한다.
    • 특정 지역 변수와 로직을 run 의 람다 블록으로 묶어서 작업하고 그것을 명시하고 싶을 때

0개의 댓글

Powered by GraphCDN, the GraphQL CDN