[Kotlin] 스코프 함수(Scope Functions)


작년에 코틀린으로 안드로이드 앱 제작을 해보면서 코틀린에 대한 이해도가 많이 부족하다는 것을 깨달았다.
그래서 이번에 부스트코스의 코틀린 강의를 들으면서 공부를 하고 있는데 안드로이드 개발을 할 때 많이 헷갈렸던 스코프 함수에 대해 정리해보았다.



스코프 함수(Scope Functions)란?

  • 코틀린 표준 라이브러리에 포함된 객체 Context내의 코드 블록을 실행시켜주는 함수

코틀린에 존재하는 스코프 함수는 다음과 같다.

Function Object reference Retrun value Is extension function
let it Lambda result Yes
run this Lambda result Yes
run - Lambda result No: called without the context object
with this Lambda result No: takes the context object as an argument.
apply this Context object Yes
also it Context object Yes

출처 - 공식문서

함수 정의

let

  • 확장함수 형태로 객체( it )를 코드블록으로 넘기고 블록의 결과값을 반환
public inline fun <T, R> T.let(block: (T) -> R): R { ... return block(this) }

also

  • 확장함수 형태로 객체( it )를 코드블록으로 넘기고 객체를 반환
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this}

apply

  • 확장함수 형태로 객체( this )를 코드블록으로 넘기고 객체를 반환
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }

run

  • 인자가 없는 익명 함수 형태로 블록의 결과값을 반환
  • 확장함수 형태로 객체( this )를 코드블록으로 넘기고 블록의 결과값을 반환
public inline fun <R> run(block: () -> R): R = return block()
public inline fun <T, R> T.run(block: T.() -> R): R = return block()

with

  • 인자가 있는 익명 함수 형태로 인자로 받는 객체( this )를 코드블록으로 전달하고 결과값을 반환
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()



함수의 특징

let과 also

letalso함수는 상당히 비슷하다. 무슨차이가 있는 걸까?

다음 예제코드를 통해 알 수 있다.

var n = 1
n = n.let { it + 3 }
println(n) // 4

var m = 1
m = m.also { it + 3 }
println(m) // 1

let함수는 코드블록 ( {...} )의 결과값( it + 3 )을 반환하고 also함수는 코드블록 실행 후 자기자신( m )을 반환 받는다.

run과 with

익명 함수 형태의 runwith는 인수의 유무만 다르고 동작이 같다.

with는 단독 함수로 사용되어 세이프 콜(?.)을 지원하지 않기 떄문에 let과 같이 사용해서 세이프 콜을 해줄 수 있다. (확장함수 형태의 스코프 함수들은 세이프 콜 지원)

supportActionBar?.let {
    with(it) {
        setDisplayHomeAsUpEnabled(true)
        setHomeAsUpIndicator(R.drawable.ic_clear_white)    
    }
}

letwith를 같이 쓰면 인수가 있는 run 효과를 낼 수 있다.

사용예제

fun main() {
    data class Person(var name: String, var skills: String)
    var person = Person("Kildong", "Kotlin")

    val returnObj = person.apply {
        this.name = "Sean"
        skills = "Java" //this 생략가능
        "success"   //사용되지 않음
    }   //returnObj : Person

    var returnObj2 = person.run {
        this.name = "Dooly"
        skills = "C#"   //this 생략가능
        "success"   //반환
    }   //returnObj2 : String

    val returnObj3 = with(person) {
        this.name = "Dooly"
        skills = "C#"   //this 생략가능
        "success"   //반환
    }   //returnObj3 : String

    val returnObj4 = person.let {
        it.name = "Jarry" //it 생략 불가능
        it.skills = "Kotlin"
        "success"   //반환
    }   //returnObj4 : String

    val returnObj5 = person.also {
        it.name = "Kildong" //it 생략 불가능
        it.skills = "Java"
        "success"   //사용되지 않음
    }   //returnObj5: Person
}

댓글남기기