문제 : 여러 값이 들어있는 Map을 제공해 객체를 초기화 하고 싶다
- 해법
코틀린의 Map에는 대리자가 되는데 필요한
getValue,setValue함수 구현이 있다
객체 초기화에 필요한 값이 맵 안에 있다면, 해당 클래스 속성을 자동으로 맵에 위임할 수 있다. 예를들면 아래와같은 Project클래스가 있다고 하자.
data class Project(val map: MutableMap<String, Any?>) {
val name: String by map
val priority: Int by map
val completed: Boolean by map
}
fun main() {
val project = Project(mutableMapOf( "name" to "Learn Kotlin", "priority" to 5, "completed" to true))
println(project.name)
println(project.priority)
println(project.completed)
}
객체의 변수이름과 같은 키가 매핑이 되어 위임된다.(거의 Decodable수준이다). 이 코드가 동작하는 이유는 MutableMap에 ReadWriteProperty 대리자가 되는 데 필요한 올바른 시그니처의 setValue, getValue확장함수가 있기 때문이다.
이게 필요한 이유를 예를 들어보면, 필요한 속성이 JSON 문자열에 있고, 이를 Gson을 이용해 파싱하여 그결과로 얻은 맵을 바로 Project로 생성할 수 있기 때문이다. 즉 아래와 같은 코드의 동작이 가능하다는것
private fun getMapFromJson() =
Gson().fromJsonM<MutableMap<String, Any?>>(
"""{ "name":"Learn Kotlin", "priority":5, "completed": true } """,
MutableMap::class.java
)
fun main() {
val project = Project(getMapFromJson())
println(project.name)
println(project.priority)
println(project.completed)
}
문제 : 어떤 클래스의 속성이 다른 클래스의 획득자와 설정자를 사용하게끔 만들고 싶다.
- 해법
ReadOnlyProperty, 또는ReadWriteProperty를 구현하는 클래스를 생성함으로써 직접 속성 대리자를 작성한다.
사용자 정의 속성 대리자를 생성하려면, ReadOnlyProperty, 또는 ReadWriteProperty인터페이스의 존재하는 함수를 제공해야 한다. 기본적으로는 그런데, 대리자를 만들기위해 인터페이스를 구현할 필요 없이 동일한 시그니처를 가진 setValue, getValue함수만 구현해주어도 된다.
class MyDelegate {
operator fun getValue(thisRef: Any?, property:KProperty<*>): String {
return "$thisRef, thank you for delegating `${property.name}` to me!!!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to `${property.name}` in $thisRef")
}
}
class Example {
var p: String by MyDelegate()
}
fun main() { val e = Example()
println(e.p)
e.p = "NEW"
}
위 코드에서 MyDelegate는 함수구현만 했을 뿐인데도, 위임자로서의 역할을 할 수 있으며 해당 코드의 실행 결과는 아래와 같다
Example@22d8cfe0, thank you for delegating `p` to me!!!
NEW has been assigned to `p` in Example@22d8cfe0