App노자

[Kotlin] by 키워드 본문

Android/Kotlin

[Kotlin] by 키워드

앱의노예 2023. 8. 6. 20:46

1. by (Delegation)란?


by키워드 코틀린에서 특정클래스를 확장하거나 이용할 수 있도록 위임을 할 때 사용한다

by를 사용하면 하나의 클래스가 다른 클래스에 위임하도록 선언하여

위임된 클래스가 가지는 멤버를 참조 없이 호출할 수 있다

<var, val, class> 프로퍼티 혹은 클래스 이름: 자료형 by 위임자

by키워드는 프로퍼티 위임에도 사용되는데 게터와 세터를 특정 객체에게 위임하고

그 객체가 값을 읽거나 쓸 때 수행하도록 만드는 것을 프로퍼티 위임이라고 한다

프로퍼티 위임을 위해서는 val, var와 같은 프로퍼티 선언을 위한 키워드, 프로퍼티의 이름, 자료형, by위임자가 필요하다

여기서 위임자란 프로퍼티나 클래스를 대신할 객체를 말한다

2. Class 위임


interface Car {
    fun go(): String
}

class VanImpl(val power: String): Car {
    override fun go() = "는 짐을 적재하며 $power 마력을 가집니다."
}

class SportImpl(val power: String): Car {
    override fun go() = "는 경주용에 사용되며 $power 마력을 가집니다."
}

class CarModel(val model: String, impl: Car): Car by impl {
    fun carInfo() {
        println("$model ${go()}") // 참조 없이 각 인터페이스 구현 클래스의 go를 접근
    }
}

fun main() {
    val myDamas = CarModel("Damas 2010", VanImpl("100마력"))
    val my350z = CarModel("350Z 2008", SportImpl("350마력"))

    myDamas.carInfo()
    my350z.carInfo()
}

인터페이스를 구현하고 있는 클래스가 있다면 정의하고 있는 모든 멤버를 클래스로 위임할 수 있다

코틀린이 가지고 있는 표준 라이브러리는 open으로 정의되지 않은 클래스를 사용하고 있는데

final형태의 클래스이므로 상속이나 확장이 어렵다

따라서 필요한 경우에만 위임을 통해 해당 클래스의 모든 기능을 사용하면서 동시 기능 추가 확장을 구현한다

 

https://kotlinlang.org/docs/delegation.html

 

Delegation | Kotlin

 

kotlinlang.org

2. 프로퍼티 위임


public object Delegates {

    public fun <T : Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()

    public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
            ReadWriteProperty<Any?, T> =
        object : ObservableProperty<T>(initialValue) {
            override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
        }
        
    public inline fun <T> vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean):
            ReadWriteProperty<Any?, T> =
        object : ObservableProperty<T>(initialValue) {
            override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue)
        }

}

프로퍼티를 위임하는 object인 Delegates로부터 사용할 수 있는 위임자인 observable함수는 프로퍼티를 감시하고 있다가

특정 코드의 로직에서 변경이 일어날 때 호출되어 처리된다 특정 변경 이벤트에 따라 호출되므로 콜백이라고도 부른다

vetoable() 함수는 observable() 함수와 비슷하지만 반환값에 따라

프로퍼티 변경을 허용하거나 취소할 수 있다는 점에서 다르다 

by lazy에 의한 지연 초기화는 스레드에 좀 더 안정적으로 프로퍼티를 사용할 수 있다

지연초기화에 사용되는 by lazy의 경우도 다음과 같이 프로퍼티의 위임이 사용된다

1. lazy람다식은 람다식을 전달받아 저장한 lazy<T>인스턴스를 반환한다

2. 최초 프로퍼티의 게터 실행은 lazy에 넘겨진 람다식을 실행하고 결과를 기록한다

3. 이후 프로퍼티의 게터 실행은 이미 초기화되어 기록된 값을 반환한다

 

https://kotlinlang.org/docs/delegated-properties.html

 

Delegated properties | Kotlin

 

kotlinlang.org

class User {
    // observable은 값의 변화를 감시하는 일종의 콜백 루틴
    var name: String by Delegates.observable("NONAME") {
        prop, old, new -> // 프로퍼티, 기존값, 새로운 값
        println("$old -> $new") // 이 부분은 이벤트가 발생할 때만 실행
    }
}

fun main() {
    val user = User()
    user.name = "Kildong" // 값이 변경되는 시점에서 첫 이벤트 발생
    user.name = "Dooly" // 값이 변경되는 시점에서 두 번째 이벤트 발생
}

클래스의 프로퍼티를 observable() 함수로 위임한다

이때 초깃값은 선언한 "NONAME"이다

값의 변경이 일어나면 작성한 println을 실행한다

값의 변경이 일어나는 시점은 fum main()에서 user.name에 새로운 값을 설정할 때이다

이때 감시 역할을 하는 observable() 함수의 코드가 수행된다

fun main() {

    var max: Int by Delegates.vetoable(0) { // 초기값은 0
        prop, old, new ->
        new > old // 조건에 맞지 않으면 거부권 행사
    }

    println(max) // 0
    max = 10
    println(max) // 10

    // 여기서는 기존값이 새 값보다 크므로 false 따라서 5를 재할당 하지 않음
    max = 5
    println(max) // 10
}

반환값에 따라 프로퍼티 변경을 허용하는 vetoable() 함수이다

초깃값으로 0을 선언하였고 프로퍼티 max를 다루고 있다

기존 값인 old보다 크면 true가 되면서 프로퍼티의 교체 작업이 진행되지만

기존 값인 old보다 작아 조건인 new > old에 성립하지 않으면 실행되지 않는다

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

[Kotlin] Closure (클로저)  (0) 2023.08.10
[Kotlin] 코틀린 표준 라이브러리  (0) 2023.08.09
[Kotlin] 지연 초기화  (0) 2023.08.05
[Kotlin] 컬렉션의 확장 함수  (0) 2023.08.04
[Kotlin] sequence (시퀀스)  (0) 2023.08.03