Kotlin, 스코프 함수

Yebali·2021년 8월 15일
0

약간의_Kotlin

목록 보기
8/19

스코프 함수

스코프 함수는 함수형 언어의 특징을 좀 더 쉽게 사용하기 위해 기본으로 제공하는 함수들이다.

인스턴스들을 스코프함수와 함께 사용하면 좀 더 깔끔하고 명확한 코딩이 가능하다.

대표적인 스코프 함수로는 apply, with, let, also, run이 있다. 알아보자!

apply

apply의 구현은 아래와 같다.

inline fun <T> T.apply(block: T.() -> Unit): T {
    block()
    return this
}

수신된 객체를 그대로 반환하며 람다함수 내부에서 자기 자신을 this로 처리한다.

람다함수 내부에서 객체의 함수를 사용하지 않고 자기 자신을 다시 반환하려는 경우 apply를 사용한다.

대표적인 예로 객체 초기화에 많이 사용된다.

data class Person (var name: String, var age: Int)

class Animal {
    var spec: String = ""
    var age: Int = 0
}

fun main() {
    val person = Person("yeseong", 29)

    val person2 = person.apply {
        this.name = "yebali2"
        age = 19 // this 생략 가능
    } // 자기 자신을 반환하기 때문에 person === person2

    println("person = ${person}")   // name=yebali2, age=19 출력
    println("person2 = ${person2}") // name=yebali2, age=19 출력
    
    //=================//
    var animal = Animal().apply {
        spec = "dog"
        age = 3
    } // 객체 초기화
}

with

with의 구현은 아래와 같다.

inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block()
}

반환 값은 람다 함수를 구성하는 가장 마지막 코드의 반환값 이며, 람다함수 안에서 this를 사용할 수 있다.

Non-nullable객체이고 주로 별도의 결과가 필요하지 않은 경우 with를 사용한다.

data class Person (var name: String, var age: Int)

fun main() {
    val person = Person("yeseong", 29)

    var age = with(person) {
        this.name = "yebali2"
        age = 19

        age // 해당 값이 반환된다.
    } // 꼭 어떤 값을 반환 받아서 사용하지 않아도 된다.

    println("person = ${person}") // name=yebali2, age=19 출력
    println("age = ${age}") // 19 출력
}

let

let의 구현은 아래와 같다.

inline fun <T, R> T.let(block: (T) -> R): R {
    return block(this)
}

let은 객체를 람다함수 내부에서 it으로 받아 사용하며, 반환 값은 람다 함수의 가장 마지막 코드의 반환값이다.

주로 다음과 같은 경우에 사용한다.

  1. 지정된 객체가 null이 아닌 경우에 코드를 실행해야 하는 경우

  2. Nullable객체를 다른 Nullable 객체로 변환하는 경우,

  3. 단일 지역 변수의 범위를 제한하는 경우

data class Person (var name: String, var age: Int)

fun main() {
    val nullPerson: Person? = null
    val person = Person("yeseong", 29)

    // This instance is null 출력
    nullPerson?.let {
        println(it)
    } ?: println("This instance is null")

    // name=yeseong, age=29 출력
    person?.let {
        println(it)
    } ?: println("This instance is null")
}

also

also의 구현은 아래와 같다.

inline fun <T> T.also(block: (T) -> Unit): T {
    block(this)
    return this
}

also는 객체를 람다 함수 내부에서 it으로 받아 사용하며, 받은 객체를 그대로 반환한다.

람다 함수 내부에서 수신된 객체를 전혀 사용하지 않거나 객체의 속성을 변경하지 않는 경우 사용한다.

예를 들면 프로퍼티에 데이터를 할당하기 전에 해당 데이터에 유효성을 검사 할 때 매우 유용하다.

data class Person (var name: String, var age: Int)

class Book(author: Person) {
    // author를 초기화 할 때 also를 이용한 유효성 검사.
    val author = author.also {
        requireNotNull(it.age)
        print(it.name)
    }
}

fun main() {
    val person = Person("yeseong", 29)
    val book = Book(person)
}

run

run의 구현은 아래와 같다

inline fun <T, R> T.run(block: T.() -> R): R {
    return block()
}

run은 객체를 전달받는 경우와 전달받지 않는 경우로 구분된다.

객체를 전달받은 경우는 전달받은 객체를 람다 함수 내부에서 this로 받아 처리하며, 객체를 전달받은 것과 상관 없이 반환 값은 람다 함수의 마지막 코드이다.

run은 어떤 값을 계산할 필요가 있거나 여러 개의 지역 변수의 범위를 제한할 때 사용한다.

data class Person (var name: String, var age: Int)

fun main() {
    var person = run {
        val p = Person("yeseong", 29)
        p
    }

    println("person = ${person}") // name=yeseong, age=29 출력

    val str = person.run {
        this.name = "yesoeng2"
        age = 19

        "Updated"
    }

    println("person = ${person}") // name=yesoeng2, age=19 출력
    println("str = ${str}") // Updated 출력
}

with와 also의 차이점?

inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    return receiver.block()
}
inline fun <T> T.also(block: (T) -> Unit): T {
    block(this)
    return this
}

with와 also는 다음과 같은 차이점을 갖습니다.

  1. 호출 시, 수신 객체가 어떻게 전달되는가?
    • with : 수신 객체가 매개변수 T로 명시적으로 제공된다.
    • also : T의 확장함수로 암시적으로 제공된다.
  1. 전달된 수신 객체가 다시 수신 객체 람다에 어떻게 전달 되는가?
    • with : 수신 객체 지정 람다가 T의 확장함수 형태로 코드블록 내에 암시적으로 전달된다.
    • also : 수신 객체 지정 람다에 매개변수 T로 코드블록 내에 명시적으로 전달된다.
  1. 최종 반환 값은 무엇인가?
    • with : 람다를 실행한 결과 (마지막 줄)
    • also : 전달된 수신객체를 그대로 다시 반환

결론

스코프 함수는 인스턴스의 함수나 변수를 스코프 내에서 깔끔하게 처리할 수 있다는 장점이 있다.

profile
머리에 다 안들어가서 글로 적는 중

0개의 댓글