우리가 필요로 할 때마다 수동으로 구현할 수 있지만 일반적으로 한 번 구현하고 라이브러리에 넣는 것이 매우 좋을 수있는 일반적인 종류의 속성이 있습니다. 예 :
- 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 to ‘p’ in Example@33a17727.
위임 된 객체에 대한 요구 사항은
확인할 수 있습니다 .코틀린 1.1에서는 함수 또는 코드 블록 내에 위임 된 속성을 선언 할 수 있으므로 반드시 클래스의 멤버가 아니어야합니다. 아래
찾을 수 있습니다 .
표준 위임
코틀린 표준 라이브러리는 몇 가지 유용한 대리인을위한 팩토리 메서드를 제공합니다.
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
초기 값과 수정을위한 핸들러라는 두 개의 인수를 취합니다. 처리기는 속성에 할당 할 때마다 ( 할당이 수행 된
후에)
호출됩니다 . 세 개의 매개 변수가 있습니다 : 속성이 할당되고 이전 값과 새 값이 지정됩니다.
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
과제를 가로 채고 거부하도록하려면
대신에 사용하십시오
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' 카테고리의 다른 글
코틀린 - 중첩 클래스 및 내부 클래스 (0) | 2021.07.13 |
---|---|
코틀린 - 위임 (0) | 2021.07.13 |
코틀린 - 제네릭 (0) | 2021.04.26 |
코틀린 - sealed 클래스 (0) | 2021.04.26 |
코틀린 - 데이터 클래스 (0) | 2021.04.25 |