[Kotlin] #범위 지정 함수 - let, apply, run, with, also

2022. 4. 14. 22:51
728x90
반응형

💡 범위 지정 함수란? (= Scope Function)


범위 지정 함수는 특정 범위 블록 내에서는 특정 객체를 this, it을 통해 접근하거나 조작할 수 있는 함수를 말한다.

즉, 범위 지정 함수를 사용하면 임시 Scope가 형성된다.

이렇게 사용하면 뭐가 좋냐?

일단 가독성이 증가하여 유지보수가 쉽고, 불필요한 변수 선언을 방지할 수 있다.

Kotlin에서는 범위 지정 함수 5가지(let, apply, with, also, run)를 제공하고 있다.

이 함수들은 수신 객체를 어떻게 전달하는지, 반환 값이 무엇인 지에 따라 차이점이 있다.

 

💡 Type 1 : let 함수


fun<T, R> T.let(block : (T) -> R) : R

let은 수신 객체를 람다의 Parameter로 전달하기 때문에 'it'을 이용해 수식 객체에 접근할 수 있다.

그리고 Block의 마지막 줄을 반환한다.

아래의 예제 코드를 보면 Block의 마지막인 "!!"를 출력하는 것을 볼 수 있다.

var str: String = "Hello"

str = str.let {
    it.plus(" World")
    "!!"
}

println(str)

 

하지만 보통 let의 경우 '?' 연산자와 함께 Null Check를 할 때 자주 사용한다.

var str1: String? = null
var str2: String? = "Hello"

str1?.let { println(str1) }
str2?.let { println(str2) }

'?' 연산자의 역할에 대해 모른다면 아래의 포스터를 참고하자.

https://coderand.tistory.com/entry/Kotlin-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C%EC%9D%98-Null-%EC%B2%98%EB%A6%ACNull-Safety

 

[Kotlin] #코틀린에서의 Null 처리 - Null Safety

💡 개요 코드를 작성하다 보면 NullPointerException(NPE)를 자주 접할 수 있다. 이 예외는 Null 값을 참조하는 경우에 발생하는 예외이다. 생각보다 NPE는 자주 발생하며 코드가 길어질수록 그 원인을 파

coderand.tistory.com

 

 

💡 Type 2 : apply 함수


fun <T> T.apply(block : T.() -> Unit): T

apply는 수신 객체를 람다의 수신 객체로 전달하기 때문에 this를 이용해 수식 객체에 접근하거나

특정한 명시를 하지 않아도 수식 객체에 접근할 수 있다.

그리고 수신 객체 그 자체를 반환한다.

그래서 어떤 객체의 프로퍼티를 수정하고 싶을 때 유용하게 사용할 수 있다.

아래 예제 코드를 보면 Person객체에 대한 프로퍼티를 수정해서 출력하는 것을 볼 수 있다.

또한, this로 수신 객체에 접근할 수도 있고, 아무런 명시가 없어도 수신 객체에 접근할 수 있는 것을 알 수 있다.

fun main() {
    val P = Person().apply {
        this.name = "Chris" // this로 접근
        age = 20 // 아무런 명시 없이 접근
    }

    println(P.toString())
}

data class Person(var name: String = "", var age: Int = 0) {
}

 

💡 Type 3 : run 함수


// 1. 수신객체 없이 익명함수로 사용
fun<R> run(block : () -> R) : R

// 2. 수신객체를 호출하여 사용
fun<T, R> T.run(block : T.() -> R) : R

run은 수신 객체를 지정하지 않고 익명 함수로 사용하거나 수신 객체를 호출하여 사용할 수 있다.

하지만 반환 값은 Block의 마지막 줄로 동일하다.

우선 익명 함수로 사용할 경우엔 내부에 수신 객체를 명시해줘야 한다.

fun main() {
    val P = Person("Chris", 20)

    val result = run {
        P.age = 25 // 구체적 명시
        false // 반환값
    }

    println(P)
    println(result)
}

data class Person(var name: String = "", var age: Int = 0) {
}

 

수신 객체를 이용할 땐 수신 객체 그 자체로 전달하기 때문에 명시하지 않거나 'this'를 이용해 수신 객체에 접근할 수 있다.

fun main() {
    val P = Person().run {
        this.name = "Chris" // this로 접근
        age = 20 // 명시 없이 접근
        true // 반환값
    }

    println(P.toString())
}

data class Person(var name: String = "", var age: Int = 0) {
}

 

💡 Type 4 : with 함수


fun<T, R> with(receiver : T, block T.() -> R) : R

with은 run과 똑같이 동작한다.

하나의 차이점이라면 run은 확장 함수로써 사용이 되지만, with은 수식 객체를 Parameter로 받아 사용한다.

위에 run의 예제 코드와 똑같이 사용하면 다음과 같이 작성할 수 있다.

fun main() {
    val P = Person("", 0)

    val result = with(P){
        this.name = "Chris" //this로 접근
        age = 20 // 명시 없이 접근
        true // 반환값
    }

    println(P.toString())
    println(result)
}

data class Person(var name: String = "", var age: Int = 0) {
}

run과 똑같이 사용되기 때문에 코드의 간결성을 위해서는 run을 사용하는 것이 좋다.

 

💡  Type 5 : also 함수


fun<T> T.also(block: (T) -> Unit): T

also는 수신 객체를 람다의 Parameter로 전달하기 때문에 'it'을 이용해 수식 객체에 접근할 수 있다.

그리고 수신 객체 그 자체를 반환한다.

apply와 유사하게 프로퍼티를 수정할 때 사용할 수 있는데, 보통 수정뿐 아니라 추가적인 작업이 필요할 때 사용한다.

fun main() {
    var num = 1
    val P = Person().also {
        it.name = "Chris" //it으로 접근
        it.age = 20
        num += 1
    } // 수신 객체 그 자체를 반환

    println(P.toString())
    println(num)
}

data class Person(var name: String = "", var age: Int = 0) {
}

728x90
반응형

'Language > Kotlin' 카테고리의 다른 글

[Kotlin] #코틀린에서의 Null 처리 - Null Safety  (0) 2022.04.13
[Kotlin] #반복문 사용법  (0) 2022.04.05
[Kotlin] #var와 val의 차이점  (0) 2022.03.29

BELATED ARTICLES

more