문제 : 여러 값이 들어있는 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