Kotlin문법: "스코프 함수(Scope Funtion)"

SHY(code poet)·2024년 2월 7일

✨Kotlin에 관하여✨ 

목록 보기
14/36

● 스코프 함수(Scope Function)= 영역함수
: 코드를 축약해서 표현할 수 있도록 도와주는 함수

  • 사용법은 함수처럼 쓰지 않고 run, let처럼 괄호 없이 일종의 키워드 같이 사용할 수 있다.
  • (lateinit과 함께 safe call 남용을 막아주는 역할도 하기에 많이 사용하는 요소)
    ⇒ 자신의 함수 스코프(코드 블록)안에서 호출한 대상을 this와 it으로 대체해서 사용할 수 있다.

① this와 it으로 구분하기
: 스코프 함수는 자신을 호출한 대상을 this 또는 it으로 대체해서 사용할 수 있는데, 스코프 함수의 사용법을 다음 두 가지로 구분할 수 있다.
※‘자신을 호출한 대상’: 위 코드에서 list.let 과 같이 list가 let을 호출함과 동시에 let 함수에서 list 안에 규정된 기능(변수,함수)들을 호출해서 사용할 것이므로 list를 this 혹은 it으로 대체할 것이다.

a. this로 사용되는 스코프 함수: run, apply, with
: 호출 대상이 스코프 함수 안에서 this로 사용되기 때문에 메서드나 프로퍼티를 직접 호출.
var list = mutableListOf("Scope", "Function")

ⅰ. run
: 스코프 함수 안에서 호출한 대상을 this로 사용할 수 있다.
클래스 내부의 함수를 사용하는 것과 동일한 효과이기 때문에 this.는 생략하고 메서드나 프로퍼티를 바로 사용할 수 있다.
다음 예제에서는 MutableList를 run 함수를 사용해서 스코프를 지정한 후 내부에서 size 프로퍼티를 직접 호출하였다.
var list = mutableListOf("Scope", "Function")
list.run {
val listSize = size // 즉, list.size라고 써 줄 필요가 없다는 것이다!
println("list의 길이 run = $listSize")
}

ⅱ. apply

list.apply {
val listSize = size
println("리스트의 길이 apply = $listSize")
}

ⅲ. with

with (list){
val listSize = size
println("리스트의 길이 with = $listSize")
}

  • 만약, 호출 대상이 null일 경우
    with는 스코프 함수이긴 하지만 위의 2개와는 다르게 확장(Extension) 함수가 아니기 때문에 일반 함수처럼 사용된다.
    따라서 호출하는 대상이 null일 경우에는 with보다는 apply나 run을 사용하는 것이 효율적이다.
    target?.apply { / 코드 / }

b. it으로 사용되는 스코프 함수: let, also

ⅰ. let
: 함수 영역 안에서 호출한 대상을 it으로 사용할 수 있다.
it.을 생략할 수는 없지만 target 등 다른 이름으로 바꿀 수 있다.
다음 예제에서는 MutableList의 size 속성을 let 스코프 안에서 it.size로 호출하였다.

var list = mutableListOf("Scope", "Function")
list.let { // it -> 생략된 형태. it -> 대신에 target -> 등으로 변경 가능합니다.
val listSize = it.size // 모든 속성과 함수를 it.멤버로 사용할 수 있습니다. //
list.size라고 써 줄 필요가 없다는 것이다!
println("리스트의 길이 let = $listSize")
}

var list = mutableListOf("Scope", "Function")
list.let { target -> // it을 target 등과 같이 다른 이름으로 변경 가능합니다.
val listSize = target.size // target으로 변경했기에 멤버 접근은 target.속성 입니다.
println("리스트의 길이 let = $listSize")
}

ⅱ. also

list.also {
val listSize = it.size
println("리스트의 길이 also = $listSize")
}

② 반환값으로 구분하기
: 위에서 사용해본 것만으로는 this와 it으로 사용되는 함수가 2개 이상씩 중복으로 있는 것처럼 보일 수 있다.
하지만 동일하게 this로 사용되는 함수라도 대입 연산자를 사용해서 값을 반환할 경우에는 용도가 달라진다.
결괏값을 반환할 경우, 스코프가 종료되는 시점에서의 반환값이 다르기 때문에 서로 다른 역할을 하는 스코프 함수가 필요하다.

a. 호출 대상인 this 자체를 반환하는 스코프 함수: apply, also
apply를 사용하면 스코프 함수 안에서 코드가 모두 완료된 후 자기 자신을 되돌려 준다.
다음 예제에서 apply 스코프의 마지막 줄에서 count()를 호출했지만 마지막 코드와 상관없이 그냥 MutalbeList 자신을 돌려주기 때문에 Scope, Function에 Apply가 추가된 값이 출력된다.
also 또한 동일하게 동작한다.

var list = mutableListOf("Scope", "Function")

val afterApply = list.apply {
add("Apply") // this. 은 생략가능하다고 했다.
count()
}
println("반환값 apply = $afterApply") // 반환값 apply = [Scope, Function, Apply]

val afterAlso = list.also {
it.add("Also")
it.count()
}
println("반환값 also = $afterAlso") // 반환값 also = [Scope, Function, Apply, Also].

b. 마지막 실행 코드를 반환하는 스코프 함수: let, run, with
let, run, with의 결괏값을 반환하는 경우에는 앞의 2개와는 완전히 다른 결과가 나올 수 있으므로 주의해야 한다.
자기 자신이 아닌 스코프의 마지막 코드를 반환하기 때문.
다음 예제의 let에서 스코프 마지막 코드가 it.count()로 종료되었다.
apply나 also라면 마지막 코드에 상관없이 printIn을 실행할 것이기 때문에 Scope, Function, Run이 출력되지만 let은 스코프 내의 마지막 코드가 반환되기 때문에 출력값으로 리스트의 개수인 3이 출력된다.
run과 with도 마지막 코드가 반환된다.

var list = mutableListOf("Scope", "Function")

val lastCount = list.let {
it.add("Run")
it.count()
}
println("반환값 let = $lastCount") // 반환값 let = 3

val lastItem = list.run {
add("Run")
get(size-1)
}
println("반환값 run = $lastItem") // 반환값 run = Run

val lastItemWith = with(list){
add("With")
get(size-1)
}
println("반환값 with = $lastItemWith") // 반환값 with = With

profile
진정한 개발자는 코드를 두려워하지 않는다. 오히려 코드가 그를 두려워한다.

0개의 댓글