본문 바로가기

프로그래밍/kotlin

코틀린 - 위임 프로퍼티

반응형

우리가 필요로 할 때마다 수동으로 구현할 수 있지만 일반적으로 한 번 구현하고 라이브러리에 넣는 것이 매우 좋을 수있는 일반적인 종류의 속성이 있습니다. 예 :

  • lazy 프로퍼티 : 값은 첫 번째 액세스시에만 계산됩니다.
  • observable 프로퍼티 : 리스너는이 속성에 대한 변경 사항에 대한 알림을받습니다.
  • 각 속성에 대한 별도의 필드 대신지도에 속성을 저장합니다.

이러한 (및 기타) 사례를 다루기 위해 코틀린은 

위임 된 속성을

 지원 합니다 .

class Example {
    var p: String by Delegate()
}

구문은 다음과 같습니다 

val/var <property name>: <Type> by <expression>

by

 다음의 표현식 은 

delegate입니다

 . 왜냐하면 속성에 해당하는 

get()

(및 

set()

)이 해당 

getValue()

및 

setValue()

메서드에 위임되기 때문 입니다. 속성 대리자는 인터페이스를 구현할 필요는 없지만 

getValue()

함수 (및 

setValue()

var

 의 경우) 를 제공해야합니다 예 :

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '$' to me!"
    }
 
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '$ in $thisRef.'")
    }
}

우리가 읽을 때 

p

의 인스턴스에 그 대표 

Delegate

의 

getValue()

에서 기능 

Delegate

의 첫 번째 매개 변수는 우리가 읽은 객체가되도록, 호출 

p

에서 두 번째 매개 변수는 설명 보유 

p

자체를 (예를 들어, 당신이 그 이름을 수 있습니다.) 예 :

val e = Example()
println(e.p)

인쇄 내용 :

Example@33a17727, thank you for delegating ‘p’ to me!

우리가에 할당 할 때 마찬가지로, 

p

의 

setValue()

함수가 호출됩니다. 처음 두 매개 변수는 동일하고 세 번째 매개 변수는 할당 할 값을 보유합니다.

e.p = "NEW"

이 지문

NEW has been assigned top’ in Example@33a17727.

위임 된 객체에 대한 요구 사항은 

아래에서

 확인할 수 있습니다 .코틀린 1.1에서는 함수 또는 코드 블록 내에 위임 된 속성을 선언 할 수 있으므로 반드시 클래스의 멤버가 아니어야합니다. 아래 

에서 예제를

 찾을 수 있습니다 .

표준 위임

코틀린 표준 라이브러리는 몇 가지 유용한 대리인을위한 팩토리 메서드를 제공합니다.

Lazy

lazy()

는 람다를 사용하고 인스턴스가 

Lazy<T>

lazy 속성을 구현하는 위임자 역할을 할 수 있는 함수를 반환하는 함수입니다. 람다 를 

get()

실행하는 첫 번째 호출 은 전달 

lazy()

된 결과를 기억하고 이후 호출은 

get()

단순히 결과를 반환합니다.

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

fun main(args: Array<String>) {
    println(lazyValue)
    println(lazyValue)
}

이 예제에서는 다음을 인쇄합니다.

computed!
Hello
Hello

기본적으로 게으른 속성의 평가는 

동기화됩니다

 . 값은 하나의 스레드에서만 계산되며 모든 스레드는 동일한 값을 볼 수 있습니다. 초기화 대리자의 동기화가 필요하지 않으면 여러 스레드가 동시에이를 실행할 수 있으므로 

LazyThreadSafetyMode.PUBLICATION

매개 변수로 

lazy()

함수에 전달 하십시오 그리고 초기화가 항상 단일 쓰레드에서만 일어날 것이라는 확신이들 경우, 

LazyThreadSafetyMode.NONE

thread-safety 보장과 관련 오버 헤드를 발생시키지 않는 mode 를 사용할 수 있습니다 .

Observable

Delegates.observable()

초기 값과 수정을위한 핸들러라는 두 개의 인수를 취합니다. 처리기는 속성에 할당 할 때마다 ( 할당이 수행 된 

후에)

 호출됩니다 세 개의 매개 변수가 있습니다 : 속성이 할당되고 이전 값과 새 값이 지정됩니다.

import kotlin.properties.Delegates

class User {
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
}

fun main(args: Array<String>) {
    val user = User()
    user.name = "first"
    user.name = "second"
}

이 예제에서는 다음을 인쇄합니다.

<no name> -> first
first -> second

과제를 가로 채고 거부하도록하려면 

vetoable()

대신에 사용하십시오 

observable()

에 전달 된 핸들러 는 새 속성 값의 할당이 수행 되기 전에

vetoable

 호출 됩니다.

도맵에 프로퍼티 저장하기

하나의 일반적인 사용 사례는 속성 값을지도에 저장하는 경우입니다. 이것은 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

이것은 읽기 전용 대신에 를 사용하면 

var

 의 속성 에도 적용 

MutableMap

됩니다 

Map

.

class MutableUser(val map: MutableMap<String, Any?>) {
    var name: String by map
    var age: Int     by map
}

로컬 위임 된 속성 (1.1 이후)

로컬 변수는 위임 된 속성으로 선언 할 수 있습니다. 예를 들어, 지역 변수를 게으르게 만들 수 있습니다 :

fun example(computeFoo: () -> Foo) {
    val memoizedFoo by lazy(computeFoo)

    if (someCondition && memoizedFoo.isValid()) {
        memoizedFoo.doSomething()
    }
}

 

memoizedFoo

변수는 상기 제 1 액세스에서만 연산한다. 

someCondition

실패 할 경우 변수는 전혀 계산되지 않습니다.

속성 대리인 요구 사항

여기에서는 객체를 위임하기위한 요구 사항을 요약합니다.A에 대한 

읽기 전용

 특성 (즉, 

 ), 대리자라는 기능을 제공하는 

getValue

다음과 같은 매개 변수를 사용합니다

  • thisRef속성 소유자 와 동일하거나 상위 유형이어야합니다 (확장 속성 - 확장 되는 유형의 경우 ).
  • property- 유형 KProperty<*>또는 상위 유형이어야합니다 .

이 함수는 속성 (또는 그 부속 유형)과 동일한 유형을 반환해야합니다.A에 대한 

가변

 속성 (a 

VAR

 ) 대리자에게 

별도로

 라는 기능을 제공하는 

setValue

다음의 파라미터를 취

  • thisRef-와 동일 getValue();
  • property-와 동일 getValue();
  • 새로운 값 - 프로퍼티 또는 그 슈퍼 타입과 같은 타입 일 필요가 있습니다.

getValue()

및 / 또는 

setValue()

함수는 델리게이트 클래스의 멤버 함수 또는 확장 함수로서 제공 될 수있다. 후자는 원래 이러한 기능을 제공하지 않는 객체에 속성을 위임해야 할 때 편리합니다. 두 기능 모두 

operator

키워드 로 표시해야 합니다.Delegate 클래스가 인터페이스를 구현할 수 

ReadOnlyProperty

및 

ReadWriteProperty

필요한 함유하는 

operator

방법. 이러한 인터페이스는 Kotlin 표준 라이브러리에 선언되어 있습니다.

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

번역 규칙

모든 위임 된 속성에 대한 후드 아래에서 Kotlin 컴파일러는 보조 속성을 생성하고이 속성에 위임합니다. 예를 들어, 속성의 

prop

경우 숨겨진 속성 

prop$delegate

이 생성되고 접근 자의 코드는이 추가 속성에 단순히 위임합니다.

class C {
    var prop: Type by MyDelegate()
}

// this code is generated by the compiler instead:
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)
}

코 틀린 컴파일러에 대한 모든 필요한 정보를 제공 

prop

인자에서 : 첫번째 인수가 

this

외부 클래스의 인스턴스를 지칭 

C

과 

this::prop

의 반사 목적은 

KProperty

설명 유형 

prop

자체.코드에서 직접 호출

this::prop

 할 수있는 참조를 참조하는 구문 은 Kotlin 1.1 이후에만 사용할 수 있습니다.

델리게이트 제공 (1.1 이후)

 

provideDelegate

연산자를 정의하면 속성 구현이 위임 된 객체를 만드는 논리를 확장 할 수 있습니다. 오른쪽에 사용 된 객체 가 멤버 또는 확장 함수로 

by

정의 

provideDelegate

되면 해당 함수가 호출되어 속성 대리자 인스턴스가 만들어집니다.가능한 유스 케이스 중 하나는 

provideDelegate

getter 또는 setter뿐만 아니라 속성이 만들어 질 때 속성 일관성을 검사하는 것입니다.예를 들어 바인딩하기 전에 속성 이름을 확인하려면 다음과 같이 작성할 수 있습니다.

class ResourceLoader<T>(id: ResourceID<T>) {
    operator fun provideDelegate(
            thisRef: MyUI,
            prop: KProperty<*>
    ): ReadOnlyProperty<MyUI, T> {
        checkProperty(thisRef, prop.name)
        // create delegate
    }

    private fun checkProperty(thisRef: MyUI, name: String) { ... }
}

fun <T> bindResource(id: ResourceID<T>): ResourceLoader<T> { ... }

class MyUI {
    val image by bindResource(ResourceID.image_id)
    val text by bindResource(ResourceID.text_id)
}

의 매개 변수 

provideDelegate

는 다음과 

getValue

같습니다.

  • thisRef속성 소유자 와 동일하거나 상위 유형이어야합니다 (확장 속성 - 확장 되는 유형의 경우 ).
  • property- 유형 KProperty<*>또는 상위 유형이어야합니다 .

이 

provideDelegate

메소드는 

MyUI

인스턴스 생성 중에 각 속성에 대해 호출 되며 필요한 검증을 즉시 수행합니다.속성과 대리자 간의 바인딩을 가로채는이 기능이 없으면 동일한 기능을 얻기 위해 명시 적으로 속성 이름을 전달해야하지만 매우 편리하지는 않습니다.

// Checking the property name without "provideDelegate" functionality
class MyUI {
    val image by bindResource(ResourceID.image_id, "image")
    val text by bindResource(ResourceID.text_id, "text")
}

fun <T> MyUI.bindResource(
        id: ResourceID<T>,
        propertyName: String
): ReadOnlyProperty<MyUI, T> {
   checkProperty(this, propertyName)
   // create delegate
}

생성 된 코드에서 

provideDelegate

메서드는 보조 

prop$delegate

속성 을 초기화하기 위해 호출됩니다 프로퍼티 선언 

val prop: Type by MyDelegate()

을 위해 생성 된 코드를 

 의 생성 된 코드와 비교합니다 

provideDelegate

메소드가 없을 때 ).

class C {
    var prop: Type by MyDelegate()
}

// this code is generated by the compiler 
// when the 'provideDelegate' function is available:
class C {
    // calling "provideDelegate" to create the additional "delegate" property
    private val prop$delegate = MyDelegate().provideDelegate(this, this::prop)
    val prop: Type
        get() = prop$delegate.getValue(this, this::prop)
}

이 

provideDelegate

메서드는 보조 속성 만들기에만 영향을 주며 getter 또는 setter에 대해 생성 된 코드에는 영향을주지 않습니다.

반응형

'프로그래밍 > kotlin' 카테고리의 다른 글