Kotlin의 위임된 프로퍼티(Delegated Properties)
Kotlin의 위임된 프로퍼티(Delegated Properties)는 프로퍼티의 getter와 setter를 다른 객체에 위임할 수 있는 강력한 기능입니다.
이 가이드에서는 위임된 프로퍼티의 다양한 측면을 상세히 살펴보겠습니다.
기본 개념 (Basic Concept)
위임된 프로퍼티는 다음과 같은 문법으로 선언합니다:
class Example {
var p: String by Delegate()
}
여기서 by
키워드 다음에 오는 표현식이 델리게이트입니다. 프로퍼티의 get()
(그리고 set()
)은 델리게이트의 getValue()
와 setValue()
메서드에 위임됩니다.
import kotlin.reflect.KProperty
class Delegate {
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.")
}
}
표준 델리게이트 (Standard Delegates)
Kotlin 표준 라이브러리는 몇 가지 유용한 델리게이트를 제공합니다.
지연 프로퍼티 (Lazy Properties)
lazy()
함수를 사용하여 지연 초기화를 구현할 수 있습니다:
val lazyValue: String by lazy {
println("computed!")
"Hello"
}
fun main() {
println(lazyValue)
println(lazyValue)
}
이 코드는 첫 번째 접근 시에만 "computed!"를 출력하고, 그 이후에는 저장된 값을 반환합니다.
관찰 가능한 프로퍼티 (Observable Properties)
Delegates.observable()
을 사용하여 프로퍼티 변경을 관찰할 수 있습니다:
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<no name>") {
prop, old, new ->
println("$old -> $new")
}
}
fun main() {
val user = User()
user.name = "first"
user.name = "second"
}
다른 프로퍼티에 위임 (Delegating to Another Property)
프로퍼티는 다른 프로퍼티에 위임될 수 있습니다:
var topLevelInt: Int = 0
class ClassWithDelegate(val anotherClassInt: Int)
class MyClass(var memberInt: Int, val anotherClassInstance: ClassWithDelegate) {
var delegatedToMember: Int by this::memberInt
var delegatedToTopLevel: Int by ::topLevelInt
val delegatedToAnotherClass: Int by anotherClassInstance::anotherClassInt
}
var MyClass.extDelegated: Int by ::topLevelInt
이 기능은 예를 들어 하위 호환성을 유지하면서 프로퍼티 이름을 변경할 때 유용할 수 있습니다.
맵에 프로퍼티 저장 (Storing Properties in a Map)
프로퍼티 값을 맵에 저장하는 것은 JSON 파싱 등의 동적 작업에서 흔히 사용됩니다:
class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
로컬 위임된 프로퍼티 (Local Delegated Properties)
로컬 변수도 위임될 수 있습니다:
fun example(computeFoo: () -> Foo) {
val memoizedFoo by lazy(computeFoo)
if (someCondition && memoizedFoo.isValid()) {
memoizedFoo.doSomething()
}
}
이는 계산 비용이 큰 값을 필요할 때만 초기화하는 데 유용합니다.
프로퍼티 델리게이트 요구사항 (Property Delegate Requirements)
프로퍼티 델리게이트는 특정 규약을 따라야 합니다. 읽기 전용 프로퍼티(val)의 경우 getValue()
함수를, 읽고 쓸 수 있는 프로퍼티(var)의 경우 추가로 setValue()
함수를 제공해야 합니다.
위임된 프로퍼티의 번역 규칙 (Translation Rules for Delegated Properties)
컴파일러는 위임된 프로퍼티를 위해 보조 프로퍼티를 생성합니다:
class C {
var prop: Type by MyDelegate()
}
// 컴파일러가 생성하는 코드:
class C {
private val prop$delegate = MyDelegate()
var prop: Type
get() = prop$delegate.getValue(this, this::prop)
set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}
델리게이트 제공 (Providing a Delegate)
provideDelegate
연산자를 정의하여 프로퍼티 델리게이트 인스턴스 생성 로직을 확장할 수 있습니다:
class ResourceLoader<T>(id: ResourceID<T>) {
operator fun provideDelegate(
thisRef: MyUI,
prop: KProperty<*>
): ReadOnlyProperty<MyUI, T> {
checkProperty(thisRef, prop.name)
// 델리게이트 생성
return ResourceDelegate()
}
private fun checkProperty(thisRef: MyUI, name: String) { ... }
}
class MyUI {
fun <T> bindResource(id: ResourceID<T>): ResourceLoader<T> { ... }
val image by bindResource(ResourceID.image_id)
val text by bindResource(ResourceID.text_id)
}
이 기능은 프로퍼티 초기화 시점에 추가적인 로직을 실행하고 싶을 때 유용합니다.
Kotlin의 위임된 프로퍼티는 코드의 재사용성과 유연성을 크게 향상시키는 강력한 기능입니다. 이를 통해 프로퍼티의 접근자 로직을 분리하고, 공통적인 패턴(예: 지연 초기화, 변경 감지)을 쉽게 구현할 수 있습니다.
주요 이점:
- 코드 재사용: 공통 로직을 델리게이트로 추출하여 재사용할 수 있습니다.
- 관심사의 분리: 프로퍼티의 접근과 저장 로직을 분리할 수 있습니다.
- 유연성: 런타임에 프로퍼티의 행동을 동적으로 변경할 수 있습니다.
- 표준 패턴: 지연 초기화, 관찰 가능한 프로퍼티 등의 패턴을 쉽게 구현할 수 있습니다.
그러나 위임된 프로퍼티를 과도하게 사용하면 코드의 복잡성이 증가할 수 있으므로, 적절한 상황에서 신중하게 사용해야 합니다. 특히 성능에 민감한 부분에서는 위임으로 인한 오버헤드를 고려해야 합니다.
위임된 프로퍼티는 특히 프레임워크나 라이브러리 개발, 복잡한 비즈니스 로직 구현, 설정 관리 등의 시나리오에서 그 진가를 발휘합니다. 이를 효과적으로 활용하면 더 유지보수가 쉽고 확장 가능한 코드를 작성할 수 있습니다.
Kotlin, 위임된프로퍼티, DelegatedProperties, 지연초기화, 관찰가능프로퍼티, 프로퍼티델리게이트, 코드재사용, 디자인패턴, 람다함수, 리플렉션, 안드로이드개발, 서버개발, 함수형프로그래밍, 성능최적화, 리팩토링
'개발&프로그래밍' 카테고리의 다른 글
[Kotlin] 함수 (0) | 2024.08.18 |
---|---|
[Kotlin] 타입 별칭(Type Aliases) (0) | 2024.08.18 |
[Kotlin] 위임(Delegation) (0) | 2024.08.17 |
[Kotlin] 객체 표현식과 선언(object expressions and object declarations) (0) | 2024.08.17 |
[kotlin] 인라인 값 클래스(Inline Value Classes) (0) | 2024.08.15 |
댓글