카테고리 없음

코틀린 스코프 함수 정리

hyuckkim 2023. 4. 15. 18:19

모든 코드는 Kotlin Playground에서 실행했습니다.

https://play.kotlinlang.org/

 

Kotlin Playground: Edit, Run, Share Kotlin Code Online

 

play.kotlinlang.org

data class User(var name: String)

우선 테스트를 위해 대충 만들어진 data class 하나.

 

 

스코프 함수는 객체에 바로 접근하여 scope 내에서 해당 객체의 이름 대신 키워드로 변수를 사용할 수 있게 한다. 일종의 기능이 있는 do while(false) 라고 생각하면 될 것 같다.

 

스코프 함수가 필수인 건 아님. 일종의 Syntex sugar. (하지만 취업하려면 필수일 것이다)

 

총 다섯 종류 let, apply, also, run, with이 있다.

- 당연히 모두 Generic이다.

- with 빼고는 모두 확장함수이다. (with은 객체를 인자로 받는 일반 함수이다)

- with 빼고는 null safe 연산자를 사용할 수 있다. 연산자를 사용했을 때 객체가 null이었으면 실행되지 않는다. with은 확장함수가 아니기 때문에 null safe 연산자를 사용할 구석이 없다.

    null.let {
        println("null is executed")
    }
    null?.let {
        println("null with null-safe operator is not executed")
    }

위는 실행되고 아래는 실행되지 않는다.

 

    var rightUser: User? = User("John Doe");
    rightUser.let {
        println("fine user throw error")
        it.name
    }
    rightUser?.let {
        println("fine user with null-safe operator is executed")
        it.name
    }

nullable한 값에서 null-safe 연산자를 사용하지 않으면 오류를 던진다.

scope 함수에서 가져오는 객체를 사용하지 않으면 오류 없이 실행이 되긴 하는데 그럴거면 scope 함수 왜씀?

    var nullUser: User? = null;
    nullUser.let {
        println("null user throw error")
        it.name
    }
    nullUser?.let {
        println("null user with null-safe operator is not executed")
        it.name
    }

객체가 null이면 실행되지 않는다.

당연.

 

 

스코프 함수별로 객체를 가리키는 키워드가 다른데, it과 this가 있다.

this는 class에서의 그 this와 같아서 스코프 안에 같은 이름의 변수가 없다면 생략할 수 있다.

 

it이든 this든 하여간 원래 변수 이름보다는 짧을 것이기 때문에 (파이썬에서 range-based loop를 처음 봤을 때를 생각하자) 코드 길이를 줄이는 데 도움이 될 것이다.

 

 

scope 안에서 함수를 하나만 사용하고, 그 함수가 인수를 하나만 받고, 그 인수에 객체를 넣을 거면 사용할 수 있는 형태가 있다.

    var rightUser: User? = User("John doe")
    var q = rightUser.let(::println)

 

 

let

    var a = User("a").let {
        println(it.name)
        it.name = "v"
        println(it.name)
    }
    println(a)
a
v
kotlin.Unit

- 확장함수

- scope 안에서 it으로 사용

- 마지막 식의 결과를 반환함 (여기서는 println의 결과인 Unit을 반환함)

 

 

 

 

apply

    var b = User("b").apply {
        println(this.name)
        name = "w"
        println(name)
    }
    println(b)
b
w
User(name=w)

- 확장함수

- scope 안에서 this로 사용

- 객체를 다시 반환함 (원본을 수정하는 거임)

 

처음 본 인상으로는 원본에서 뭔가 바꾼 새 객체를 만드는 줄 알았는데 그거는 아니다. 

    var testUser = User("John doe")
    var testUser2 = testUser.apply {
        this.name += " the disester"
    }
    println(testUser)
    println(testUser2)
User(name=John doe the disester)
User(name=John doe the disester)

 

 

 

 

 

also

    var c = User("c").also {
        println(it.name)
        it.name = "x"
        println(it.name)
    }
    println(c)
c
x
User(name=x)

- 확장함수

- scope 안에서 it으로 사용

- 객체를 다시 반환함 (얘도 원본 수정)

 

it을 쓴다는 걸 빼면 apply와 똑같아 보임.

 

 

 

 

 

run

    var d = User("d").run {
        println(this.name)
        name = "y"
        println(name)
    }
    println(d)
d
y
kotlin.Unit

- 확장함수

- scope 안에서 this로 사용

- 마지막 식의 결과를 반환함

 

let의 this 버전.

 

 

 

 

with

    var e = with(User("e")) {
        println(this.name)
        name = "z"
        println(name)
    }
    println(e)
e
z
kotlin.Unit

- 확장함수가 아님

- scope 안에서 this로 사용

- 마지막 식의 결과를 반환함

 

 

 

    var rightUser: User? = User("w?o?man")
    var p = with(rightUser) {
        print(name)
    }
    var q = with(rightUser) {
        print(this?.name)
    }

확장함수가 아니기 때문에 with 안의 this는 nullable이다. this를 생략하고 사용하면 오류를 던진다.

this?.name으로만 써야 한다.

 

 

 

뭔가 동작이 전부 같아 보이지만 내가 감히 추측해보기론, 현업에서는 전부 역할을 구분해두고 쓸 것이다.

(어떤 동작에서는 let, 어떤 동작에서는 run 하는 식으로)

 

 

Reference

https://blog.yena.io/studynote/2020/04/15/Kotlin-Scope-Functions.html

 

[Kotlin] 코틀린 let, with, run, apply, also 차이 비교 정리

let, with, run, apply, also 코틀린에는 이렇게 생긴 확장함수들이 있다. 객체를 사용할 때 명령문들을 블럭{} 으로 묶어서 간결하게 사용할 수 있게 해주는 함수들이다. 문제는 서로 비슷비슷해서 헷

blog.yena.io

 

https://kotlinlang.org/docs/scope-functions.html

 

Scope functions | Kotlin

 

kotlinlang.org

 

유광무 팀장님의 'Hello, Android!' 설명.