Kotlin Series Eight - Commissioned

Overview

  • class delegate

The delegate pattern has proven to be a good alternative to implementing inheritance, and Kotlin supports it natively with zero boilerplate code. This is the official description of delegation. It may be abstract, but when you understand its meaning and use, you will find that the convenience brought by delegation will make you fall in love with it. Let's start learning delegation. Let's first look at an official example:

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(b: Base) : Base by b

The above-mentioned Derived class inherits the interface Base and should implement the methods in the interface Base, but instead of implementing by b here, because the object passed in by the main constructor of Derived's class itself implements the Base interface, here The by means that b will be stored internally in Derived, and the compiler will generate all methods of Base that are forwarded to b. A more intuitive understanding of this sentence is that the methods executed by the created Derived object are delegated to b, which is equivalent to calling the method of object b (the method that implements the Base interface will be delegated).

Add a method to the Derived and BaseImpl classes to see which method gets called at this point:

class BaseImpl(val x: Int) : Base {
    override fun print() { Log.e("================",x.toString()) }

    fun log(){
        Log.e("================","BaseImpl===============")
    }
}

class Derived(b: Base) : Base by b{
    fun log(){
    Log.e("================","Derived===============")
}
}

Create a Derived object, and call the print() and log() methods respectively, and run the results:

02-28 02:20:40.062 4226-4226/com.example.administrator.kotlinpractise E/================: 10
02-28 02:20:40.062 4226-4226/com.example.administrator.kotlinpractise E/================: Derived===============

Here you can see that the print method China outputs the method in BaseImpl, and the log output is the Derived method. Now you know the sense of happiness in the sentence in the last bracket above.

  • Delegate property:
The delegated object must provide the getValue (and setValue) method, because when the delegated object is called, the system will automatically call the delegated object's get(), and when the delegated negative value, its set method will be called
class  Example {
    var e: String by Delege ()
}

class Delege{
    operator fun getValue(thisRef: Any?, property: KProperty<*>) : String {
        Log.e("getValue============","getValue")
        return "$thisRef"
    }
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String){
        Log.e("setValue============","$value")
    }

Now create an object with negative value Example:

var example = Example()
Log.e("=========", example.e)
example.e = "New Example"
Log.e("=========", example.e)

Now take a look at the printed log:

02-28 03:02:04.826 5876-5876/com.example.administrator.kotlinpractise E/getValue============: getValue
02-28 03:02:04.826 5876-5876/com.example.administrator.kotlinpractise E/=========: com.example.administrator.kotlinpractise.Example@3d2e49e
02-28 03:02:04.827 5876-5876/com.example.administrator.kotlinpractise E/setValue============: New Example
02-28 03:02:04.827 5876-5876/com.example.administrator.kotlinpractise E/getValue============: getValue
02-28 03:02:04.827 5876-5876/com.example.administrator.kotlinpractise E/=========: com.example.administrator.kotlinpractise.Example@3d2e49e
For a read-only property (that is, declared by val), the delegate must provide a function called getValue that accepts the following parameters:
thisRef - must be the same as the property owner type (for extended properties - the type being extended) or its supertype;
property - must be of type KProperty<*> or its supertype.
This function must return the same type (or its subtype) as the property.
For a mutable property (ie var declared), the delegate must additionally provide a function named setValue, which accepts the following parameters:
thisRef - same as getValue();
property - same as getValue();
new value - required The same type as the property or its supertype.

The getValue() or/and setValue() functions can be provided by member functions of the delegate class or by extension functions. The latter is more convenient when you need to delegate properties to objects that don't otherwise provide these functions. Both functions need to be marked with the operator keyword.

  • Observable property Observable

Accepts two parameters: the initial value and the handler on modification. This handler is called whenever we assign a value to the property (executed after the assignment). It has three parameters: the assigned property, the old value and the new value, and the delegated program executes each time the variable is assigned a value

var observer : String by Delegates.observable("A"){
    property,old,new ->
    Log.e("$old=====","$new")
}
Log.e("=========",observer)
observer = "test"
Log.e("=========",observer)

Output result:

02-28 02:51:36.356 5496-5496/com.example.administrator.kotlinpractise E/=========: A
02-28 02:51:36.356 5496-5496/com.example.administrator.kotlinpractise E/A=====: test
02-28 02:51:36.356 5496-5496/com.example.administrator.kotlinpractise E/=========: test

  • vetoable()
Before the property is assigned a new value , the handler passed to the vetoable will be called . You can choose to accept or reject the assignment according to the conditions. Parameters: the initial value and the judgment program ( requires a boolean value to be returned), when the return is true, the assignment is successful, false reject the assignment    
var observer: String by Delegates.vetoable("A") { property, old, new ->
     new.equals("NEW")
}
Log.e("=========", observer)
observer = "test"
Log.e("=========", observer)
observer = "NEW"
Log.e("=========", observer)

Output result:

02-28 03:02:04.827 5876-5876/com.example.administrator.kotlinpractise E/=========: A
02-28 03:02:04.827 5876-5876/com.example.administrator.kotlinpractise E/=========: A
02-28 03:02:04.827 5876-5876/com.example.administrator.kotlinpractise E/=========: NEW

  • store the property in the map

A common use case is to store property values ​​in a map. This is often seen in applications like parsing JSON or doing other "dynamic" things. In this case, you can implement the delegated property using the map instance itself as the delegate.

class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
In this example, the constructor accepts a map parameter:
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))

The delegated property will take the value from this map (via the string key - the name of the property):

println(user.name) // Prints "John Doe"println(user.age) // Prints 25
This also works for var properties, if you replace the read-only Map with a MutableMap :            
class MutableUser ( val map : MutableMap < String , Any ?> ) {
var name : String by map
var age : map by int
}


At this point, everyone should have a preliminary understanding of delegation. Personally, I feel that delegation is still very practical in development. For example, for some data that needs to be read and set, we can use attribute delegation to avoid some code and have better logic. I will continue to publish a more in-depth application of delegation, so let's stop here for the time being.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325756829&siteId=291194637