위임 프로퍼티
기본 위임 프로퍼티
- lazy 같은 것을 의미한다.
- lazy는 여러 버전의 구현이 있다.
LazyThreadSafetyMode로 이를 정할 수 있다.- SYNCHRONIZED: 프로퍼티 접근을 동기화한다. 따라서 한 번에 한 쓰레드만 프로퍼티 값을 초기화 할 수 있다.(default)
- PUBLICATION: 초기화 함수가 여러 번 호출될 수 있지만 가장 처음 도착한 결과가 프로퍼티 값이 되도록 둔다.
- NONE: 프로퍼티 접근을 동기화 하지 않는다. 쓰레드 safe하지 않다.
- 초기화 함수가 예외를 던지면 초기화되지 않는다.
kotlin.properties.Delegates의 멤버를 통해서 몇 가지 표준 위임을 사용할 수 있다.NotNull은 함수 프로퍼티 초기화를 미루면서 Null이 아닌 프로퍼티를 정의할 수 있게 해준다. (lateinit과 결과적으로 같다.)observable은 변화에 따라 통지받을 수 있다.vetoable은 observable과 비슷하지만 초깃값을 받고 boolean을 뱉는 람다를 받는 인자를 가진다. 변경 시도에 따라 변경 전 이 람다가 콜되고 true면 변경된다.beforeChange,afterChange등도 있다.
커스텀 위임 프로퍼티
- 커스텀 위임을 만들고자 한다면 읽고 쓰는 방법을 구현해야 한다.
- 연산자 함수들을 정의하는 특별한 타입이 필요하다. 읽기 함수 이름은
getValue여야 하고receiver,property를 받는다. - receiver는 수신 객체 값이 들어 있고, 위임된 프로퍼티의 수신 객체와 같은 타입이어야 한다.
- property는 프로퍼티 선언을 표현하는 리플렉션이 들어 있다.
KProperty<*>이거나 상위 타입이어야 한다. - 이 두 파라미터는 이름보다 타입이 중요하다.
getValue()는 반드시 위임 프로퍼티 타입과 같거나 하위 타입이어야 한다.
import com.sun.org.apache.xml.internal.security.Init
import kotlin.reflect.KProperty
class Cachable<in R, out T : Any>(val init: R.() -> T) {
private val map = HashMap<R, T>()
operator fun getValue(receiver: R, property: KProperty<*>): T {
return map.getOrPut(receiver) { receiver.init() }
}
}
fun <R, T : Any> cached(init: R.() -> T) = Cachable(init)
class Person(val firstName: STring, val familyName: String)
val Person.fullName: String by cached { "$firstName $familyName" }
//여기서 읽기 전용을 만들고 싶다면 `kotlin.properties.ReadOnlyProperty`
- 읽고 쓰는 것을 정의하려면
getValue(),setValue()를 정의해야 한다. receiver, property, newValue 세 가지 파라미터를 받는다 - kotlin 1.1부터는
provideDelegate()함수를 통해서 위임 인스턴스화를 제어할 수 있다. 기본적으로 프로퍼티 선언 뒤의 by 키워드 다음에 오는 식을 통해 위임 인스턴스를 정의한다.
위임 표현
- 러타임에 위임은 별도의 필드에 저장한다. 반면 프로퍼티 자체에 대해서는 접근자가 자동으로 생성된다. 이 접근자는 위임에 있는 적절한 메소드를 호출한다. ```kotlin class Person (val firstName: String, val familyName: String ) { var age: Int by finalLateInit() } //이 코드는
class Person (val firstName: String, val familyName: String ) {
private val age$delegate = finalLateInit<Person, Int>()
var age: Int
get() = age$delegate.getValue(this, this::age)
set(value) {
age$delegate.setValue(this, this::age, value)
}
}
```