Skip to content

배토의 개발일지

나를위한 단편적 기억들의 기록

Menu
  • 안드로이드
  • 코틀린
  • Godot
  • 블렌더
  • git
  • 게임제작일지
  • 기타
Menu

Kotlin: Delegated Properties

Posted on 2020년 10월 20일 by batmask

Kotlin 공식 문서를 보고 정리했다. 공식 문서 참조.

property들의 값을 get(), set()으로 구현해야 하는 경우, 같은 형태의 property가 필요하면, 각 property마다 get(), set()을 구현해야 한다. 이런 경우, 한번만 구현해서 중복을 제거하면 좋을 것이다. 이는 delegate를 통해 가능해진다. 예를 들면 다음과 같은 케이스들이 존재한다.

  • lazy properties : 값을 처음 가져올 때, 생성 및 계산이 이루어짐.
  • observable properties : 값의 변경시 listener들이 알림을 받음.
  • storing properties in a map : property들의 값을 map에 저장해놓고 가져오는 경우.

이런 경우들을 위해, Kotlin에서는 delegated properties를 지원한다.사용법은 다음과 같다.

class Example {
    // syntax : val/var <property name>: <Type> by <expression>
    var p: String by Delegate()
}

‘by’ 키워드가 사용되고 property의 get(), set()은 Delegate 클래스의 getValue(), setValue()에게 위임(delegated)된다. Delegate 클래스는 다음과 같다.

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 standard library에서는 유용한 delegate들을 표준으로 제공한다.

Lazy

lazy()는 함수이다. 표준 라이브러리문서를 찾아보면 다음과 같다.

fun <T> lazy(initializer: () -> T): Lazy<T>

처음 get()이 호출될 때, 기술한 람다함수가 실행되고 property에 저장된다. 그 다음 부터 get()이 호출되면, 저장된 값이 불려지게 된다.

val lazyValue: String by lazy {
    println("computed!")
    "Hello"
}

fun main() {
    println(lazyValue)
    println(lazyValue)
}
computed!
Hello
Hello

Observable

Property에 대한 observer 패턴은 자주 사용된다. 이 경우, Delegates.observable()을 사용하면 편해진다. 레퍼런스를 찾아보면, 다음과 같이 정의된다.

inline fun <T> observable(
    initialValue: T,
    crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit
): ReadWriteProperty<Any?, T>

첫번째 파라미터는 기본값을 받고, 두번째는 onChange에 해당하는 함수를 받는다. property, old, new 3개의 argument를 갖게 되는데, 값 변경시 호출되는 부분이다. 실제 구현은 다음과 같다.

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"
}

보면 알 수 있듯이, observer 패턴 전체가 구현되진 않고, observer 패턴이 필요한 경우, 쉽게 구현할 수 있도록 해준다.


Delegating to another property

Kotlin 1.4 부터 다른 property의 getter, setter로 delegate가 가능하다. delegate 받는 property 앞에 “::” 를 붙여주어 사용한다. 예제는 다음과 같다.

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

공식문서에서 좋은 예를 보여주는데, property 이름을 변경하면서 이전 버전의 호환성을 유지하는 경우이다. 이전 property의 구현을 새 propery에게 위임해주면된다. 예제는 다음과 같다.

class MyClass {
   var newName: Int = 0
   @Deprecated("Use 'newName' instead", ReplaceWith("newName"))
   var oldName: Int by this::newName
}

fun main() {
   val myClass = MyClass()
   // Notification: 'oldName: Int' is deprecated.
   // Use 'newName' instead
   myClass.oldName = 42
   println(myClass.newName) // 42
}

Storing properties in a map

앞서 말했듯, map 에 property들을 저장하는 경우, 사용할 수 있다. 이 경우, delegate 대상으로 map의 인스턴스를 직접 사용하면된다. 이렇게 구현시, property를 참조하면 map으로부터 값을 가져온다. 예제는 다음과 같다.

class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}

생성자에 map이 정의되어 있고, property인 name과 age는 map에 delegate하고 있다. User 인스턴스를 하나 생성해보자.

val user = User(mapOf(
    "name" to "John Doe",
    "age"  to 25
))

map으로 초기화 시켰지만, 사용은 propery로 이용가능하다.

println(user.name) // Prints "John Doe"
println(user.age)  // Prints 25

추가적인 내용이 더 있지만, 당장은 이정도면 충분하여 생략한다. 처음 언급한대로 공식문서를 정리한 내용이니 이를 참고하면된다.

답글 남기기 응답 취소

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

최신 글

  • Blender : snap을 써서 다른 vertex에 정렬하기
  • KTor Client 기본 사용 정리
  • 게임 만들거다.
  • Using Koin with Android Compose

보관함

2020 10월
일 월 화 수 목 금 토
 123
45678910
11121314151617
18192021222324
25262728293031
« 5월   12월 »

메타

  • 로그인
  • 엔트리 피드
  • 댓글 피드
  • WordPress.org
©2025 배토의 개발일지 | Built using WordPress and Responsive Blogily theme by Superb