Android Development with Kotlin - Delegates |
이 정리글은 Kotlin in Action 책을 보고 실무에 Kotlin 을 사용하던 사람이 몰랐던 내용이나 remind 하고 싶은 내용을 위주로 정리한 글입니다.
제대로 내용을 파악하시려면 책을 구매해서 읽어보세욤~
-
1 | class WitcherPlayer(enemy: String) : Player by RpgGamePlayer(enemy){ } |
위 코드를 통해 Player 에 정의된 Delegate 가능한 모든 method 는 RpgGamePlayer 로 delegate 가 된다.
단, implemented 된 녀석은 delegate 되지 않는다.
RpgGamePlayer 는 init 타임에 instantiate 가 된다.
아래와 같은 delegate 도 가능하다.
1 2 3 4 | class WitcherPlayer(player:Player) : Player by player val d = RpgGamePlayer( 10 ) class WitcherPlayer(a:Player) : Player by d |
-
여러 개의 interface 를 상속한 경우 여러개의 delegate 도 가능하다.
1 2 3 4 | 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 을 구현해야 한다.
1 2 3 4 5 6 7 8 9 | 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 이다.
1 | 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 이 불린다.
1 2 3 4 | 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 할지 안 할지 결정할 수 있다.
1 2 3 | var list:List<String> by Delegates.vetoable(emptyList()){ _, old, new -> new .size > old.size // 이 조건을 만족시켜야 set 한다 } |
이는 validate 하기 좋음
-
Kotlin 의 Map 은 getValue 와 setValue 가 Key 값을 기준으로 구현되어 있다.
그래서 property 를 map 에 delegate 할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 를 이용하면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <p>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)</p> |
사용처에서는 textViewVisibility property 에 접근해서 Boolean 값만 바꾸어주면 된다.
-
Kotlin 1.1 부터는 provideDelegate 라는 operator 를 사용할 수 있다.
class initialization 하는 동안 delegate 를 제공하는 데 사용할 수 있다.
1 2 3 4 5 6 7 | 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 가 호출되는 구조이다.
'프로그래밍 놀이터 > Kotlin, Coroutine' 카테고리의 다른 글
[Coroutine] Coroutine context and dispatchers (4) | 2019.02.08 |
---|---|
[android] AsyncTask 를 Coroutine 으로 바꿔본 후기 (0) | 2019.02.07 |
[도서 정리] Android Development with Kotlin - Extension Functions and Properties (0) | 2018.12.16 |
[도서 정리] Android Development with Kotlin - Generics Are Your Friends (0) | 2018.12.15 |
[도서 정리] Android Development with Kotlin - Functions as First-Class Citizens (0) | 2018.12.14 |
댓글