Kotlin学习之委托

什么是委托?

就是我让A去做一件事情,A自己不去做,而是把这个事情委托给B去完成,B是A的委托。这个是委托的基本概念。

类委托

interface MyInterface {
    fun myPrint()
}

 实现这个接口

class MyInterfaceImpl(val str: String) : MyInterface {
    override fun myPrint() {
        println("welcome" + str)
    }

}

接下来看下委托的基本实现方式

MyClass不打算自己去实现MyInterface的 myPrint()方法,而是委托给MyInterfaceImpl这个类的实例。实现方法如下:

class MyClass (myInterface: MyInterface):MyInterface by myInterface{

}

MyClss里面的 myPrint()的实现整个的委托给了构造方法的参数myInterface去做。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main12)
        val myInterfaceImpl = MyInterfaceImpl("张三")
        myInterfaceImpl.myPrint()

        val myClass = MyClass(myInterfaceImpl).myPrint()
    }

委托原理:by关键字后面对象实际上会被存储在类的内部,编译器则会将父接口中的所有方法实现出来,并且将实现转移给委托对象去进行。

也就是by后面的myInterface会被存储在MyClass 的内部,编辑器是会把父接口中的所有方法实现出来,实现的逻辑就是直接调用委托的实现方法。

委托属性

如果在一个类里面定义了一个属性,我们可以给属性赋值,也可以获取到这个值,相当于set()/get(),已经由Kotlin编译器生成了。如果采用了委托属性的概念的话,被委托的属性的set()和get()不是由Kotlin编译器生成,而是转移给委托的那一方。

class MyDelegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name}' in $thisRef.")
    }
}

格式有严格要求,必须叫getValue(),setValue()

 getValue():第一个参数是对象、第二个参数是属性

 setValue():第一个参数是对象、第二个参数是属性,第三个参数是值

class MyPropertyClass {
    var str: String  by MyDelegate()
}
class MyPropertyClass {
    var str: String  by MyDelegate()
}
  override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main13)
        val myPropertyClass = MyPropertyClass()
        myPropertyClass.str = "hello world"//调用set方法
        println(myPropertyClass.str)//调用get方法
    }

委托属性有四种情况在开发中比较有用:

1.延迟属性 Lazy

第一次访问的时候会执行一个计算,当你第二次访问的时候就不计算了,而是把第一次计算的结果缓存起来,每一次直接获取结果。

2.可观察属性 Observable

当你给一个属性赋值的时候,这个属性相当于有一个监听器一样,在赋值之前和赋值之后,监听器会收到通知,执行一些预先处理或者事后处理。

3.非空属性

属性不能为空

4.map属性

属性名和属性值,相当于key和value。类中的所有属性委托给一个map对象统一的管理,而不是每个存在单独的字段中。比如支付宝的支付,当你在用户支付完以后,支付宝的服务器会给你自己的服务器返回一个异步的通知,支付宝的参数是动态的,请求参数的结构是动态的,我们就可以声明一个map。

接下来举例说明下

延迟属性 Lazy

val myLazyValue :Int by lazy {
    println("hello world")
    30
}
  override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main14)

        println(myLazyValue)
        println(myLazyValue)
    }

 第一次访问的时候会执行lamda表达式,当第二次访问的时候,值已经被计算过一次,30作为lamda表达式的值,不会再去执行lamda表达式。

可观察属性 Observable

class Person {
    var age: Int by Delegates.observable(20) { property, oldValue, newValue ->
        println("${property.name},oldValue:$oldValue,newValue:$newValue")
    }
}
  override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main15)
        val person = Person()
        person.age = 30
        person.age = 40
        println("----------")

    }

class Person2 {
    var age: Int by Delegates.vetoable(20) { property, oldValue, newValue ->
        when {
            oldValue <= newValue -> true
            else -> false
        }
    }
}
 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main15)
        val person2 = Person2()
        println("${person2.age}")
        println("----------")

        person2.age = 40
        println("${person2.age}")
        println("----------")

        person2.age =30
        println("${person2.age}")

    }

Delegates.observable接收2个参数分别是初始值和修改处理器

处理器会在我们每次对属性赋值时得到调用,在赋值之后调用。

处理器本身接收3个参数分别是被赋值的属性本身,旧的属性值和新的属性值

Delegates.vetoable的调用时机和Delegates.observable恰好相反,它是在属性被赋值之前被调用

根据Delegates.vetoable的返回值是true还是false来决定真正对属性进行赋值

map委托

将属性存储到map中。

一种常见的应用场景是出现在Json解析或者是一些动态行为。

在这种情况中,你可以将map实例作为委托,作为类中属性的委托。

只读属性的委托

class Student(map: Map<String, Any?>) {
    val name: String by map
    val address: String by map
    val age: Int by map
    val birthday: Date by map
}
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main15)
        val student = Student(
            mapOf(
                "name" to "zhangsan",
                "address" to "hangzhou",
                "age" to 20,
                "birthday" to Date()
            ))

        println(student.name)
        println(student.address)
        println(student.age)
        println(student.birthday)
    }

map中的key的名字要和属性的名字要一致

读写属性的委托

class Student2(map: MutableMap<String, Any?>) {
    var address:String by map
}
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main15)
        val map: MutableMap<String, Any?> = mutableMapOf(
            "address" to "beijing"
        )
        val student2 = Student2(map)
        println(student2.address)
        println(map["address"])

        student2.address = "shanghai"
        println(student2.address)
        println(map["address"])
    }

非空属性

适用于没有办法初始化阶段确定初始值的场合

class MyPerson {
    var address: String by Delegates.notNull<String>()
}
   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main14)
        val myPerson = MyPerson()
        myPerson.address = "hangzhou"
    }

关于属性委托的要求

对于只读属性来说(val 修饰的属性),委托需要提供一个getValue的方法,该方法接收如下参数:

- thisRef:需要是属性拥有这或者是其父类型

- property:需要是KProperty<*>类型或者是其父类型

getValue方法需要返回与其属性相同的类型或者其子类型

对于读写属性来说(var修饰的属性),委托需要提供一个名为setValue的方法,该方法需要接收如下参数:

-thisRef,与getValue方法的thisRef要求一致

-property,与property方法的thisRef要求一致

-new value,需要与属性的类型相同或者是其父类型

getValue和setValue方法既可以作为委托类的成员方法来实现,也可以作为其扩展方法来实现这俩个方法都必须要标记为operator关键字,对于委托类来说,它可以实现ReadOnlyProperty 或者ReadWriteProperty 接口,包含了响应的getValue和setValue方法。

猜你喜欢

转载自blog.csdn.net/jingerlovexiaojie/article/details/107606856