본문 바로가기
프로그래밍 놀이터/Kotlin, Coroutine

[도서 정리] Android Development with Kotlin - Delegates

by 돼지왕 왕돼지 2018. 12. 17.
반응형

Android Development with Kotlin - Delegates





이 정리글은 Kotlin in Action 책을 보고 실무에 Kotlin 을 사용하던 사람이 몰랐던 내용이나 remind 하고 싶은 내용을 위주로 정리한 글입니다.

제대로 내용을 파악하시려면 책을 구매해서 읽어보세욤~



android with kotlin, by lazy, delegate by, delegate decorator pattern, delegates, getvalue, kotlin delegate, lateinit, Lazy, non null delegate, nothing?, observable adapter notifydatasetchanged, observable delegate, operator function getvalue, operator function setvalue, predefined delegate, property delegate, property delegate example, property metadata, property name, Property Type, providedelegate, setValue, thisRef, vetoable delegate



-

class WitcherPlayer(enemy: String) : Player by RpgGamePlayer(enemy){ }


위 코드를 통해 Player 에 정의된 Delegate 가능한 모든 method 는 RpgGamePlayer 로 delegate 가 된다.

단, implemented 된 녀석은 delegate 되지 않는다.

RpgGamePlayer 는 init 타임에 instantiate 가 된다.


아래와 같은 delegate 도 가능하다.


class WitcherPlayer(player:Player) : Player by player

val d = RpgGamePlayer(10)
class WitcherPlayer(a:Player) : Player by d



-

여러 개의 interface 를 상속한 경우 여러개의 delegate 도 가능하다.

class WitcherPassionate : Player by WitcherPlayer(“monsters”),
                                GameMaker by WitcherCreator(“Witcher 3”){
    ...
}



-

Delegate 를 잘 사용하면 decorator pattern 을 사용하기에도 좋다.



-

property delegation 을 하면 property 에 대한 getter 와 setter 가 delegate 된 class 를 통해 호출된다.


property delegation 을 받는 class 는 getValue 와 setValue operator function 을 구현해야 한다.

class UserDelegate { 
    operator fun getValue(thisRef: Any?, property: KProperty<*>):User = readUserFromFile()
   
    operator fun setValue(thisRef: Any?, property: KProperty<*>,user:User) {
        saveUserToFile(user) 
    } 

    ...
}

여기서 thisRef 는 delegating 하는 property 가 속해있는 context 이다.

Activity 안에 속해있다면 thisRef 는 Activity 가 된다.

만약 delegating 하는 property 가 top-level 에 있다면 thisRef 는 Nothing? 이다.


property 값에는 property 에 대한 metadata 들이 들어가 있다.

property name, type, 등등의



-

lazy 는 predefine 된 delegate 이다.

val someProperty by lazy { SomeType() }



-

lazy delegate 는 Activity 의 view 에 사용하기도 괜찮은데.. 

그 이유는 해당 view 가 직접 사용되기 전까지 findView 할 필요가 별로 없기 때문이다. ( 상황에 따라 실제 해당 view 가 전혀 사용되지 않을수도 있다. )

또한 view 가 non-null 일 필요가 없고 val 로 설정할 수도 있다. findView 코드도 한곳에 몰려서 좋다. 



-

lazy delegate 는 intent param parsing 에 쓰기도 괜찮다.



-

notNull delegate 는 lateinit 과 동일한 녀석이다.

그러나 lateinit 이 더 빠르고 좋지만, primitives 나 top-level property 에는 사용 불가능하다. 이 때는 notNull 을 사용해야 한다.



-

observable delegate 는 variable 의 변화를 observe 해서 callback 이 불린다.

var name:String by Delegates.observable(“Empty”){ property, oldValue, newValue ->
    …
}
// “Empty” 는 기본값



-

observable 을 사용할 때 주의할 점은 property 값이 바뀐 경우가 아니라 property 의 내부값이 변경된 경우에는 observable callback 이 불리지 않는다는 것! ( 예를 들면 array 내부의 구성물이 바뀔 때 )



-

observable 을 사용하면 adapter 의 notifyDataSetChanged 를 불러주기 좋다.



-

vetoable delegate 는 observable delegate 와 거의 동일하지만 약간 다르다. (veto 는 '거부권' 뜻)

우선 lambda 가 new value set 전에 불린다. 그리고 new value 를 set 할지 안 할지 결정할 수 있다.

var list:List<String> by Delegates.vetoable(emptyList()){ _, old, new ->
    new.size > old.size // 이 조건을 만족시켜야 set 한다
}

이는 validate 하기 좋음



-

Kotlin 의 Map 은 getValue 와 setValue 가 Key 값을 기준으로 구현되어 있다.

그래서 property 를 map 에 delegate 할 수 있다.

class User(map: Map<String, Any>) { // 1 
    val name: String by map 
    val kotlinProgrammer: Boolean by map 
} 

// Usage 
val map: Map<String, Any> = mapOf( // 2 
    "name" to "Marcin", 
    "kotlinProgrammer" to true 
) 

val user = User(map) // 3 
println(user.name)  // Prints: Marcin 
println(user.kotlinProgrammer)  // Prints: true



-

MVP 에서 binding 하는 것은 아래와 같이 하면 편하다.

Presenter 를 통해서 하기 위해 interface 를 만들지 않아도 된다.

property delegate 를 이용하면 된다.

fun Activity.bindToVisibility(@IdRes viewId: Int ) = object :ReadWriteProperty<Any?, Boolean> { val view by lazy { findViewById(viewId) } override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { return view.visibility == View.VISIBLE } override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { view.visibility = if(value) View.VISIBLE else View.GONE } } var textViewVisibility:Boolean by bindToVisibility(R.id.textView)


사용처에서는 textViewVisibility property 에 접근해서 Boolean 값만 바꾸어주면 된다.



-

Kotlin 1.1 부터는 provideDelegate 라는 operator 를 사용할 수 있다.

class initialization 하는 동안 delegate 를 제공하는 데 사용할 수 있다.

class A(val i: Int) {
    operator fun provideDelegate(thisRef: Any?, prop: KProperty<*>) = object:ReadOnlyProperty<Any?, Int> {
        override fun getValue(thisRef: Any?, property: KProperty<*>) = i
    } 
} 

val a by A(1)


provideDelegate operator 를 구현하면, a 를 접근할 때 자동으로 A 의 provideDelegate 에서 제공하는 getter, setter 가 호출되는 구조이다.




반응형

댓글