728x90
- 코틀린은 코드 재사용과 과련해서 프로퍼티 위임이라는 새로운 기능을 제공
- 프로퍼티 위임을 사용하면 일반적인 프로퍼티의 행위를 추출해서 재사용 가능
- 대표적인 예로 지연 프로퍼티가 있음
- lazy 프로퍼티는 처음 사용하는 요청이 들어올 때 초기화되는 프로퍼티를 의미
- 코틀린에서는 프로퍼티 위임을 활용해 간단하게 구현할 수 있음
- stdlib는 lazy프로퍼티 패턴을 쉽게 구현할수 있는 함수 제공
val value by lazy { createValue() }
- 변화가 있을 때 감지하는 observable 패턴을 쉽게 만들 수 있음
- stdlib의 observable 델리게이트를 기반으로 간단하게 구현할 수 있음
var items: List<Item> by Delegates.observable(listOf()) { _, _, _ -> notifiyDataSetChanged() } var key: String? by Delegates.observable(null) { _, old, new -> Log.e("Key changed from $old to $new") }
- 프로퍼티 위임 메커니즘을 활용하면, 다양한 패턴들을 만들 수 있음
- 뷰, 리소스 바인딩, 의존성 주입, 데이터 바인딩 등
- 자바에서는 어노테이션을 많이 활용하지만 코틀린은 프로퍼티 위임을 사용해서 구현 가능
// 안드로이드에서의 뷰와 리소스 바인딩
private val button: Button by vindView(R.id.button)
private val textSize by bindDimension(R.dimen,font_size)
private val doctor: Doctor by argExtra(DOCTOR_ARG)
// Koin에서 종속성 주입
private val persenter: MainPresenter by inject()
private val repository: NetworkRepository by inject()
private val vm: MainViewModel by viewModel()
// 데이터 바인딩
private val port by bindConfiguration("port")
private val token: String by preferences.bind(TOKNE_KEY)
- 프로퍼티가 사용될 때 간단한 로그를 출력하는 예제
- 타입이 다르지만, 내부적으로는 거의 같은 처리를 함
var token: String? = null get() { print("token returned value $field") } set(value) { print("token changed from $field to $value") } var attempts: Int = 0 get() { print("attempts returnd value $field") } set(value) { print("attempts changed from $field to $value") field = value }
- 프로퍼티 위임을 활용해 변경한 예제
var token: String? = null
get() {
print("token returned value $field")
}
set(value) {
print("token changed from $field to $value")
}
var attempts: Int = 0
get() {
print("attempts returnd value $field")
}
set(value) {
print("attempts changed from $field to $value")
field = value
}
- 프로퍼티 위임이 어떻게 동작하는지 이해하려면, by가 어떻게 컴파일되는지 보는것이 좋음
- 컨텍스트(this)와 프로퍼티 레퍼런스의 경계도 함께 사용하는 형태로 바뀜
- 프로퍼티에 대한 레퍼런스는 이름, 어노테이션과 관련된 정보를 얻을 때 사용됨
@JvmField private val `token$delegate` = LoggingProperty<String?>(null) var token: String? get() = `token$delegate`.getValue(this, ::token) set(value) { `token$delegate`.setValue(this, ::token, value) }
- getValue와 setValue 메서드가 여러개 있어도 컨텍스트를 활용하므로, 상황에 따라서 적절한 메서드가 선택됨
- 컨텍스트의 종류에 따라서 적절한 메서드가 선택되게 만들 수 있음
class SwipeRefreshBinderDelegate(val id: Int) { private var cache: SwipeRefreshLayout? = null operator fun getValue(activity: Activity, prop: KProperty<*>): SwipeRefreshLayout { return cache ?: activity.findViewById<SwipeRefreshLayout>(id).also { cache = it } } operator fun getValue(fragment: Fragment, prop: KProperty<*>): SwipeRefreshLayout { return cache ?: fragment.view.findViewById<SwipeRefreshLayout>(id).also { cache = it } } }
- 객체를 프로터피 위임하려면 val의 경우 getValue 연산
- var의 경우 getValue, setValue 연산이 필요
- 멤버 함수로도 만들 수 있지만, 확장 함수로 만들어 사용 가능
val map: Map<String, Any> = mapOf("name" to "Marcin", "KotlinProgrammer" to true)
val name by map
print(name)
- 코틀린 stdlib에 확장 함수가 정의 되어 있음
inline operator fun <V, V1 : V> Map<in String, V>.getValue(thisRef: Any?, property:KProperty<*>): V1 = getOrImplicitDefault(property.name) as V1
- 코틀린 stdlib에 프로퍼티 델리게이터를 알아 두면 좋음
- lazy
- Delegates.observable
- Delegates.vetoable
- Delegates.notNull
- 법용적으로 사용되는 패턴들에 대한 프로퍼티 델리게이터이므로 알아 두면 좋음
정리
- 프로퍼티 델리게이트는 프로퍼티와 관련된 다양한 조작을 할 수 있으며, 컨텍스트와 관련된 대부분의 정보를 갖음
- 프로퍼티의 동작을 추출해서 재사용할 수 있음
- 표준 라이브러리의 lazy와 observable이 대표적인 예
- 프로퍼티 위임은 프로퍼티 패턴을 추출하는 일반적인 방법
728x90
'코틀린 스터디 > 이펙티브 코틀린' 카테고리의 다른 글
아이템22) 일반적인 알고리즘을 구현할 때 제네릭을 사용하라 (0) | 2024.08.08 |
---|---|
아이템20) 일반적인 알고리즘을 반복해서 구현하지 말라 (0) | 2024.04.17 |
아이템19) knowledge를 반복하여 사용하지 말라 (0) | 2024.04.17 |
아이템18) 코딩 컨벤션을 지켜라 (0) | 2024.04.17 |
아이템17) 이름 있는 아규먼트를 사용하라 (0) | 2024.04.17 |