일반적인 프로퍼티 패턴은 프로퍼티 위임(Delegated Properties)으로 만들어라
지연 프로퍼티: 이후에 처음 사용하는 요청이 들어올 때 초기화되는 프로퍼티
val value by lazy { createValue() }
import android.util.Log
import kotlin.properties.Delegates
val items: List<Item> by Delegates.observable(listOf()) { _, _, _ ->
notifyDataSetChanged()
}
val key: String? by Delegates.observable(null) { _, old, new ->
Log.e("TAG", "key changed from $old to $new")
}
위와 같이 observable을 사용해 작성한다면 내부 데이터가 변경될 때마다 변경된 내용을 출력하고, 변경 사항을 로그로 작성할 수 있다.
프로퍼티 위임을 사용해 간단하고 type-safe하게 구현할 수 있다.
var token: String? = null
get() {
print("token returned value $field")
return field
}
set(value) {
print("token changed from $field to $value")
field = value
}
var attempts: Int = 0
get() {
print("attempts returned value $field")
return field
}
set(value) {
print("attempts changed from $field to $value")
field = value
}
위의 코드는 get이나 set할때 print() 문을 남긴다는 공통점이 있다. 공통적으로 자주 반복되는 패턴은 프로퍼티 위임으로 추출하기 좋다.
getValue(), setValue()를 정의해 프로퍼티 위임을 구현할 수 있다.
var token: String? by LoggingProperty(null)
var attempts: Int by LoggingProperty(0)
private class LoggingProperty<T>(var value: T) {
operator fun getValue(
thisRef: Any?,
prop: KProperty<*>
): T {
print("${prop.name} returned value $value")
return value
}
operator fun setValue(
thisRef: Any?,
prop: KProperty<*>,
newValue: T
) {
val name = prop.name
print("$name changed from $value to $newValue")
value = newValue
}
}
val map: Map<String, Any> = mapOf(
"name" to "Marcin",
"kotlinProgrammer" to true
)
fun main() {
val name by map
print(name) // Marcin
}
inline operator fun <V, V1: V> Map<in String, V>
.getValue(thisRef: Any?, property: KProperty<*>): V1 =
getOrImplicitDefault(property.name) as V1
프로퍼티를 map에 위임하면 getValue에서 맵에 프로퍼티를 값을 저장할 때는 자동으로 프로퍼티 이름을 키로 활용한다.