본문은 Effective Kotlin을 읽고 간단하게 정리한 글입니다. 필요에 따라 생략/수정된 부분이 있을 수 있으며, 내용이 추후 변경될 수 있습니다.
1. 프로퍼티 위임이란?
1) 의미
- 다른 객체의 메서드를 활용해서 프로퍼티 접근자(getter와 setter)를 만드는 것
- 쉽게 말해 다른 객체에게 프로퍼티의 접근 구현을 맡기는 것
2) 효과
- 일반적인 프로퍼티의 행위를 추출해서 사용할 수 있다
- 프로퍼티 접근 로직을 재활용하는 데 그 의의가 있다
2. 사용하는 방법
프로퍼티 위임을 사용할 땐 위임을 받은 객체의 메서드 이름이 중요한데, getter는 getValue, setter는 setValue 함수를 이용하여 정의해야 된다. 그리고 객체를 만든 뒤엔 by 키워드를 사용해서 클래스를 연결시켜줘야 한다.
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
}
}
3. 동작원리
위 예제가 컴파일 된 코드이다. getValue와 setValue의 인수로 객체의 참조(this)와 프로퍼티의 참조(::token)가 넘겨진 것을 볼 수 있다.
@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) {
operator fun getValue(
activity: Activity,
prop: KProperty<*>,
): SwipeRefreshLayout {
// ...
}
operator fun getValue(
fragment: Fragment,
prop: KProperty<*>
): SwipeRefreshLayout {
// ...
}
}
4. 코틀린의 대표적인 프로퍼티 위임자
마지막으로 책에서 등장하는 몇 가지 대표적인 프로퍼티 위임자와 그 기능에 대해 간략하게 설명하고 마무리하고자 한다.
- lazy: 람다를 통해 호출되는 시점에 프로퍼티를 초기화한다 (지연 초기화) (lazy과 lateinit의 차이는 여기를 참고)
- Delegates.observable: 값에 변화가 생기면 콜백 함수를 수행한다
- Delegates.vetoable: 값의 변경 시 특정한 조건(콜백 함수가 Boolean을 반환)에 따라 변경을 취소할 수 있다
- Deleagates.notNull: 지연 초기화되는 프로퍼티를 반환하는 위임자
- 지연 초기화를 수행하는 점에 있어 lazy와 동일하지만 그 동작에 있어선 차이가 있다
- Delegates.notNull은 해당 프로퍼티가 바로 초기화될 수 없지만, not null임을 보장하고 싶을 때 사용된다 (초기화 되지 않거나 null이라면 에러가 발생한다)
- lazy는 콜백 함수를 실행시킴으로써 접근 시점에 프로퍼티를 초기화한다
'책 > Effective Kotlin' 카테고리의 다른 글
[이펙티브 코틀린] 아이템 43: API의 필수적이지 않은 부분을 확장 함수로 추출하라 (0) | 2023.09.04 |
---|---|
[이펙티브 코틀린] 아이템 42: compareTo의 규약을 지켜라 (0) | 2023.09.04 |
[이펙티브 코틀린] 아이템17: 이름 있는 아규먼트를 사용하라 (0) | 2023.07.23 |
[이펙티브 코틀린] 아이템16: 프로퍼티는 동작이 아니라 상태를 나타내야 한다 (0) | 2023.07.21 |
[이펙티브 코틀린] 아이템9: use를 사용하여 리소스를 닫아라 (0) | 2023.07.04 |