Delegation pattern is a basic skill in software design pattern. In delegate mode, two objects participate in processing the same request, and the objectthat accepts the request delegates the request to another object to process.
The delegate of a class, that is, a method defined in one class is actually implemented by calling the method of an object of another class. The derived class in the following example In Attribute delegation means that the attribute value of a class is not defined directly in the class, but entrusted to a proxy class, so as to realize the unified management of the attributes of this class. Attribute delegate syntax format: Attribute name: attribute name Type: data type of the attribute Expressions: delegated agent class This class needs to contain The output is as follows: A number of factory methods have been built into the standard Execute the output result: The handler that executes the event after the attribute assignment, which has three parameters: the assigned attribute, the old value, and the new value: Execute the output result: A common use case is to store the value of an attribute in a map. This oftenoccurs in applications like parsing JSON or doing other “dynamic” things.In this case, you can use the mapping instance itself as a delegate to implement the delegate property. Execute the output result: If you use the Execute the output result: It is important to note that an exception is thrown if the property is accessed before the assignment. You can declare local variables as delegate properties. For example, you caninitialize a local variable lazily: For a read-only property (that is, the val property), its delegate must provide a file named This function must return the same type (or its subtype) as the property. For a variable value (mutable) attribute (that is, Behind the implementation of each delegate property By defining For example, if you want to check the property name before binding, you can write: Before creating Without this ability to intercept the binding between a property and its delegate, you must explicitly pass the property name in order to achieve thesame function, which is not very convenient: In the generated code, the Attention please,
Kotlin
directly support the delegation model, more elegant and concise.
Kotlin
by keyword
by
implement the delegate. 3.18.1. Class delegation #
Derived
inherited the interface
Base
all methods and delegate an incoming
Base
class to execute these methods.// Create Interface
interface Base {
fun print()
}
// The delegated class that implements this interface
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
// Establishing a delegate class through the keyword by
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // Output 10
}
Derived
statement
by
clause indicates that the
b
save in
Derived
and the compiler will generate the object instance inherited from the
Base
interface and forwards the call to the
b
. 3.18.2. Attribute delegation #
val/var <Attribute Name>:<Type>by<Expression>
var/val
: Attribute type (variable/read-only)
by
keyword is the delegate, the attribute
get()
method (and
set()
method to be delegated to this object
getValue()
and
setValue()
method. Property delegates do not have to implement any interfaces, but must provide
getValue()
function (for
var
property, you also need to
setValue()
function). 3.18.3. Define a delegated class #
getValue()
methods and
setValue()
method and parameters
thisRef
is the object of the delegated class
prop
is the object of the delegated property.import kotlin.reflect.KProperty
// Define a class that contains attribute delegates
class Example {
var p: String by Delegate()
}
// Delegated Class
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, Here, the ${property. name} attribute is delegated"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$The ${property.name} attribute of thisRef is assigned a value of $value")
}
}
fun main(args: Array<String>) {
val e = Example()
println(e.p) // Access this property and call the getValue() function
e.p = "Runoob" // Calling the setValue() function
println(e.p)
}
Example@433c675d Here, the p attribute is delegated
Example@433c675d Assign the p attribute of to Runoob
Example@433c675d Here, the p attribute is delegated
3.18.4. Standard entrustment #
Kotlin
library to implement the delegate of properties. 3.18.5. Delay property Lazy #
lazy()
is a function that accepts a
Lambda
expression as an argument, returns a
Lazy
<T>
instance, and the returned instance can beused as a delegate to implement the delay property: call the
get()
execution has been passed to the
lazy()
of
lamda
expression and record the result, and then call
get()
just return the results of the record.val lazyValue: String by lazy {
println("computed!") // First call output, second call not executed
"Hello"
}
fun main(args: Array<String>) {
println(lazyValue) // First execution, execute the output expression twice
println(lazyValue) // The second execution only outputs the return value
}
computed!
Hello
Hello
3.18.6. Observable attribute
Observable
#
observable
can be used to implement the observer pattern.
Delegates.observable()
function takes two arguments: the first is the initialization value, and the second is the responder (handler) to the property value change event.import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("Initial value") {
prop, old, new ->
println("old value:$old -> new value:$new")
}
}
fun main(args: Array<String>) {
val user = User()
user.name = "First assignment"
user.name = "Second assignment"
}
Old value: Initial value ->New value: First assignment
Old value: First assignment ->New value: Second assignment
3.18.7. Store attributes in the mapping #
class Site(val map: Map<String, Any?>) {
val name: String by map
val url: String by map
}
fun main(args: Array<String>) {
// Constructor accepts a mapping parameter
val site = Site(mapOf(
"name" to "Novice Tutorial",
"url" to "www.runoob.com"
))
// Read mapping values
println(site.name)
println(site.url)
}
Novice Tutorial
www.runoob.com
var
property, you need to set the
Map
replace it with
MutableMap
:class Site(val map: MutableMap<String, Any?>) {
val name: String by map
val url: String by map
}
fun main(args: Array<String>) {
var map:MutableMap<String, Any?> = mutableMapOf(
"name" to "Novice Tutorial",
"url" to "www.runoob.com"
)
val site = Site(map)
println(site.name)
println(site.url)
println("--------------")
map.put("name", "Google")
map.put("url", "www.google.com")
println(site.name)
println(site.url)
}
Novice Tutorial
www.runoob.com
--------------
Google
www.google.com
3.18.8. Not Null #
notNull
is suitable for situations where the attribute value cannot be determined at the initialization stage.class Foo {
var notNullBar: String by Delegates.notNull<String>()
}
foo.notNullBar = "bar"
println(foo.notNullBar)
3.18.9. Local delegate attribute #
fun example(computeFoo: () -> Foo) {
val memoizedFoo by lazy(computeFoo)
if (someCondition && memoizedFoo.isValid()) {
memoizedFoo.doSomething()
}
}
memoizedFoo
variables are only evaluated on the first access. If
someCondition
if it fails, the variable will not be calculated at all. 3.18.10. Attribute delegation requirement #
getValue()
function. This function accepts the following parameters:
thisRef
- must be the same as the property owner type (for extended attributes - the type being extended) or its supertype
property
- must be a type
KProperty<*>
or its supertype.
var
property), except
getValue()
function, its delegate must provide another one named
setValue()
which accepts the following parameters:
property
- must be a type
KProperty<*>
or its supertype.
new
value
must be of the same type as the property or its supertype. 3.18.11. Translation rules #
Kotlin
compilergenerates an auxiliary property and delegates it. For example, for attributes
prop
to generate hidden properties
prop$delegate
and the code for the accessor is simply delegated to this additional propertyclass C {
var prop: Type by MyDelegate()
}
// This is the corresponding code generated by the compiler:
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)
}
Kotlin
compiler provides information in the parameters about
prop
all the necessary information: the first parameter
this
reference to aninstance of the external class C and
this::prop
KProperty
reflection object of type that describes the
prop
himself. 3.18.12. Provide entrustment #
provideDelegate
operator, you can extend the logic of creating properties to implement the delegated object. If
by
objectusedon the right will
provideDelegate
if it is defined as a member oran extension function, the function is called to create a property delegateinstance.
provideDelegate
possible usage scenario is when a property is created (not just in its
getter
or
setter
check for property consistency.class ResourceLoader<T>(id: ResourceID<T>) {
operator fun provideDelegate(
thisRef: MyUI,
prop: KProperty<*>
): ReadOnlyProperty<MyUI, T> {
checkProperty(thisRef, prop.name)
// Create Delegation
}
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
parameters same as
getValue
:
thisRef
- must be the same as the property owner type (for extended attributes - the type being extended) or its supertype
property
- must be a type
KProperty<*>
or its supertype.
MyUI
instance, call for each property
provideDelegate
method and immediately perform the necessary validation.// Check attribute names without using the 'provideDelegate' function
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 Delegation
}
provideDelegate
method to initialize the auxiliary
prop$delegate
property. Compare for attribute declarations
val
prop:
Type
by
MyDelegate()
generated code is the same as above (when
provideDelegate
code generated when the method does not exist:class C {
var prop: Type by MyDelegate()
}
// This code is when the "provideDelegate" function is available
// Code generated by the compiler:
class C {
// call “provideDelegate” to create additional “delegate“ attributes
private val prop$delegate = MyDelegate().provideDelegate(this, this::prop)
val prop: Type
get() = prop$delegate.getValue(this, this::prop)
}
provideDelegate
method only affects the creation of auxiliary properties and does not affect the
getter
or
setter
generated code.