kotlin scope function

3 minute read

코틀린 스코프 함수

  • 코틀린 표준 라이브러리는 객체의 컨텍스트 내에서 코드 블럭을 실행하기 위한 목적만을 가진 여러가지 함수를 제공한다.
  • 이런 함수들을 람다식으로 호출할 때, 이는 임시로 범위(scope) 를 형성한다.
  • 이 범위 내에서는 객체의 이름이 없어도 객체에 접근 할 수 있다. 이러한 함수를 scope function 이라고 한다.
  • 종류
    • run, let, apply, also, with
  • 차이점
    • scope 함수는 본질이 비슷하지만 각 scope 함수에는 두 가지 큰 차이점이 있다.
      • 객체의 컨텍스트를 참조하는 방식
      • 리턴 값 (this or it)

run

  • 객체의 컨텍스트를 참조하는 방식
    • 람다 수신자로써의 컨텍스트를 this 키워드로 참조한다.
    • run 을 사용할 경우 내부 scope 는 this 의 성질을 갖는다.
  • 리턴 값
    • 함수 리터럴의 마지막 expression
  • 비확장 함수로도 사용가능
    • 표현식이 필요한 곳에서 다수의 구문 블럭을 실행할 수 있도록 해준다.
  • case
    • 객체 초기화와 결과를 계산할 때
    • expression 이 필요한 곳에 statements 을 실행할 때
  • code
      class RunScope(val name: String, val age: Int)
    
      fun main() {
    
          val run = RunScope("testRun", 3)
          val plusFive = run.run {
              println("객체 컨텍스트 내의 run 실행")
              this.age + 5
              // or age + 5
          }
          println(">>> 컨텍스트 내 실행 결과 : $plusFive")
    
          // 비확장 함수로 사용
          val run1 = run {
              println("비확장 함수 실행")
              1 + 10
          }
          println(">>> 비확장 함수 실행 결과 : $run1")
    
      }
    

let

  • 객체의 컨텍스트를 참조하는 방식
    • 컨텍스트 객체를 람자 인자로 가진다.
    • 암시적인 기본 이름인 it 으로 접근이 가능하다.
  • 리턴 값
    • 함수 리터럴의 마지막 expession ( scope 의 마지막 값을 리턴)
  • case
    • null 이 아닌 객체에 람다를 실행할 때
    • 지역 scope 에서 expression을 변수로 선언할 때
  • code
      class LetScope(val name: String, val age: Int?) {
    
          override fun toString(): String {
              return "LetScope(name='$name', age=$age)"
          }
      }
    
      // top-level function
      fun isOdd(let: LetScope) : Boolean {
          if(let.age != null) {
              return let.age % 2 == 1
          }
          return false
      }
    
      fun main() {
    
          // 1. 기본 let
          val let = LetScope("letScope", 7)
          val minusSix = let.let {
              println("let 함수 실행 : $it")
              it.age?.minus(6)
          }
          println(">>> 결과 : $minusSix")
    
          // 2. 스트림 내 let
          val let2 = mutableListOf(
              LetScope("0001", 1),
              LetScope("0002", 2),
              LetScope("0003", 3))
          let2
              .filter(::isOdd) // 람다 대신 최상위 메서드 레퍼런스
              .map {
                  println("age : ${it.age}")
                  it.name.toInt() * 3
              }
              .let {
                  val test = 1
                  println("list + 1 : ${it + test}") // + 연산자의 결과로 read-only collection 이 생성된다.
              }
    
          // 3. null check
          var nullableLet: LetScope? = null
          nullableLet?.let {
              println("null check")
          }
    
          // 테스트 중에 발견했음.. 뭔지 더 자세히 알아보자!
          null?.let {
              println("?")
          }
      }
    

apply “다음의 지시를 객체에 적용하라(apply the following assignments to the object).”

  • 객체의 컨텍스트를 참조하는 방식
    • 람자 수신자로써의 컨텍스트를 this 키워드로 참조
    • apply 를 사용할 경우 내부 scope 는 this 의 성질을 갖는다.
  • 리턴 값
    • 컨텍스트 객체를 리턴한다.
  • case
    • 객체 선언
  • code
      class ApplyScope(val name: String, val age: Int) {
          fun printName() {
              println("Object name : $name")
          }
      }
    
      fun main() {
    
          // 1. 람다 수신자
          // 프로퍼티에 바로 접근이 가능하다
          // 스코프 함수 apply 의 타입이 수신 객체 타입이기 때문이다
          val applyScope: ApplyScope = ApplyScope("apply", 1).apply { println("age: $age")}
    
          // 2. 컨텍스트 객체를 반환하기 때문에 체이닝 방식으로 작성 가능
          // 메서드 참조는 중괄호 없이 사용한다
          applyScope.apply(ApplyScope::printName).apply { println("모든 프로퍼티를 출력했어요!") }
    
      }
    

also “그리고 또한 다음을 수행하라(and also do the following).”

  • 객체의 컨텍스트를 참조하는 방식
    • 컨텍스트 객테를 람자 인자로 가진다.
    • 암시적인 기본 이름인 it 으로 접근이 가능하다.
  • 리턴 값
    • 컨텍스트 객체를 리턴한다.
  • case
    • 부가적인 실행
  • code
      data class AlsoScope(val name: String, val age: Int)
    
      fun main() {
    
          // 1. 람다 객체를 인자로 받아 it 으로 접근 혹은 다른 이름 지정
          val alsoScope = AlsoScope("also", 55)
              .also { println("AlsoScope 를 생성하였습니다 : $it") }
              .also { also -> println("한번 더 출력 : $also") }
      }
    

with “이 객체로, 다음을 수행하라(with this object, do the following.).”

  • 객체의 컨텍스트를 참조하는 방식
    • 람다 수신자로써의 컨텍스트를 this 키워드로 참조
    • with 을 사용할 경우 내부 scope 는 this 의 성질을 갖는다.
  • 리턴 값
    • 함수 리터럴의 마지막 expression
  • 비확장 함수이다.
  • case
    • 객체에 대한 그룹 함수 호출
    • 헬퍼 객체의 프로퍼티나 함수를 선언
  • code
      data class WithScope(val name: String, val age: Int)
    
      fun main() {
    
          val with: WithScope = WithScope("with", 25)
    
          val result = with(with) {
              println("also Scope $name")
              1
          }
          println("${result == 1}")
    
      }
    
-->

Categories:

Updated:

Comments