티스토리 뷰

728x90

 

위임 프로퍼티

 

      1. 표준 위임

  • lazy()
    • LazyThreadSafeyMode 이넘 상수 값
      • SYNCHRONIZED: 항상 하나의 스레드에 의해서만 초기화되도록 보장한다. (default)
      • PUBLICATION: 초기화 함수가 여러번 호출될 수 있지만 가장 처음 도착하는 결과가 프로퍼티 값이 된다.
      • NONE: 프로퍼티 접근을 동기화하지 않는다. (가장 빠르며 한 스레드에서만 불린다고 확신할 수 있는 경우)
val value1 by lazy {
    println("Initializing value")
    123
}

val value2 by lazy(LazyThreadSafeyMode.PUBLICATION) {
    println("Initializing value")
    123
}
 
    • value1은 default로 SYNCHRONIZED 모드이기 떄문에 메시지가 최대 한번만 출력된다.
    • value2는 프로퍼티 값은 한번만 변경되지만 여러 스레드가 값을 초기화하려고 시도하면서 메시지가 여러번 출력될 수 있다.

 

  • notNull()
var text: String by notNull()

fun main() {
    println(text) // error
      
    text = ""
    println(text)
}
 
  • 초기화를 미루면서 널이 아닌 프로퍼티를 정의할 수 있다.
  • lateinit과 같은 기능이지만, lateinit은 primitive type에 사용될 수 없고 notNull()은 가능하다.

 

  • observable()
var name: String by observable("John") { _, old, new ->
    println("Name changed: $old to $new")
}

fun main() {
    name = "Vincent"    // Name changed: John to Vincent
    name = "Harry"      // Name changed: Vincent to Harry
}
 
    • 프로퍼티 값이 변경될 때 통지를 받을 수 있다.
    • 초깃값과 람다를 인자를 받는다.
    • 이전 값과 새 값이 같더라도, 대입 연산자를 통해 값을 write하면 통지가 온다.

 

  • vetoable()
var pwd: String by vetoable("password") { _, old, new ->
    if(new.length < 8) {
        println("Password should be at least 8 characters long")
        false
    }
    else {
        println("Password is Ok")
        true
    }
}

fun main() {
    pwd = "pAsSwOrD"    // Password is Ok
    println(pwd)        // pAsSwOrD
     
    pwd = "qwerty"      // Password should be at least 8 characters long
    println(pwd)        // pAsSwOrD
}
    • 초깃값과 Boolean을 반환하는 람다를 인자로 받는다.
    • 프로퍼티 값을 변경하기 직전 람다가 호출되며, 람다가 true를 반환하면 변경되고 false를 반환하면 변경되지 않는다.

 

  • Map 위임 객체
class MapDelegate(data: Map<String, Any?>) {
    // 프로퍼티 이름: key, 위임 객체: Map
    val str: String by data
    val i: Int by data
}

fun main() {
    val mapDelegate = MapDelegate(mapOf("str" to "Hello", "i" to 10))
    println(mapDelegate.str) // Hello
    println(mapDelegate.i)   // 10
}
 
  • 프로퍼티의 이름을 키로 사용하고, 위임 객체를 Map으로 하면 프로퍼티에 값이 저장된다.

  

 

 

      2. 커스텀 위임

  • 커스텀 위임을 만들기 위해서는 위임 클래스에 함수나 연산자 함수를 정의해야 한다.

 

  • getValue()
class Delegate(str: String) {
    var value: String = str

    operator fun getValue(receiver: Nothing?, property: KProperty<*>): String {
        return value
    }
}

fun main() {
    val p: String by Delegate("Hello")
    println(p)
}
  • val/var 위임 프로퍼티의 값을 읽을 때 호출되는 함수
  • 반환 타입: 위임 프로퍼티의 타입 또는 하위 타입
  • 파라미터
    • receiver: receiver와 같은 타입이거나 상위 타입
    • property: 위임 프로퍼티를 표현하는 KProperty<*> 리플렉션

 

  • setValue()
class Delegate {
    lateinit var value: String

    operator fun getValue(receiver: Nothing?, property: KProperty<*>): String {
        return value
    }

    operator fun setValue(receiver: Nothing?, property: KProperty<*>, newValue: String) {
        value = newValue
    }
}

fun main() {
    var p: String by Delegate()
    p = "Hello"
    println(p)
}
  • var 위임 프로퍼티에 값을 저장할 때 호출되는 함수
  • 반환 타입: Unit
  • 파라미터
    • receiver
    • property
    • newValue: 프로퍼티에 저장할 새 값. 프로퍼티와 같은 타입이거나 상위 타입

 

interface ReadOnlyProperty<in R, out T> {
    operator fun getValue(thisRef: R, property: KProperty<*>): T
}

interface ReadWriteProperty<in R, T> {
    operator fun getValue(thisRef: R, property: KProperty<*>): T
    operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}
 
  • ReadOnlyProperty/ReadWriteProperty 인터페이스를 사용해 커스텀 위임을 정의할 수 있다.

 

  • provideDelegate()
class Delegate {
    operator fun provideDelegate(receiver: Nothing?, property: KProperty<*>): Delegate {
        // 위임 객체 제어 로직
        return Delegate()
    }

    operator fun getValue(...) { ... }
}

fun main() {
    val p: String by Delegate()
}
  • 위임 객체의 인스턴스화를 제어할 수 있다.
  • 반환 타입: 실제 위임 객체

   

 

 

      3. 위임 표현

class Delegate {
    operator fun getValue(...) { ... }
    operator fun setValue(...) { ... }
}

fun main() {
    private val delegate = Delegate() // 컴파일러가 생성한 보조 프로퍼티
    var p: String
        set(value: String) = delegate.setValue(..., value)
        get() = delegate.getValue(...)
}
  • 내부적으로 컴파일러는 위임 프로퍼티의 보조 프로퍼티를 만들고, 위임 프로퍼티의 게터와 세터에 getValue(), setValue()를 넣는다.

 

  • getDelegate()
val p: String by lazy { "Hello" }

fun main() {
    val delegate = ::p
        .apply { isAccessible = true }
        .getDelegate() ?: return

    println(delegate::class.qualifiedName) // kotlin.SynchronizedLazyImpl
}
    • KProperty의 멤버 함수이며 위임 값을 얻을 수 있다.

 

  • getExtensionDelegate()
class Foo
val Foo.p: String by lazy { "Hello" }

fun main() {
    val delegate = Foo::p
        .apply { isAccessible = true }
        .getExtensionDelegate() ?: return

    println(delegate::class.qualifiedName) // kotlin.SynchronizedLazyImpl
}
 
    • 확장 프로퍼티의 위임 값을 얻을 수 있다.

 

728x90
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함