Kotlin语法(十六)-代理(Delegation)

来源:互联网 发布:alpine php 编辑:程序博客网 时间:2024/05/06 02:45

         参考原文:http://kotlinlang.org/docs/reference/delegation.html

 

      类代理(Class Delegation)

         代理模式提供一种实现继承的替代方式,Kotlin原生就支持代理模块。

         如下,“Derived”继承“Base”接口,并代理了它的全部公共方法:

interface Base {  fun print()}class BaseImpl(val x: Int) : Base {  override fun print() { print(x) }}class Derived(b: Base) : Base by bfun main(args: Array<String>) {  val b = BaseImpl(10)  Derived(b).print()  // prints 10}

         

         通过“by”关键字,将“b”实例存储到Derived对象中,编译器会生成“Base”接口的所有方法,使用“b”的实现。

 

     代理属性(Delegated Properties)

         对于很多公用属性,尽管在每次需要的时候可以通过手动实现;更好的方式是一次实现多次使用,并放到一个库(library)。

         比如,有下面类型的属性:

         Ø  延迟属性(lazy properties):只有第一次访问时才会计算值。

         Ø  观察属性(observable properties):当该属性发生改变时,会通知监听者。

         Ø  map中存储属性,不是在单独的字段中。

 

         在Kotlin中,为了满足上面几种情况,提供了代理属性(delegated properties):

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


         语法:val/var <property name>: <Type> by <expression>,“by”关键字后面的表达式就是代理(delegate);属性的“get()”和“set()”对应代理的“getValue()”和“setValue()”。属性代理不要去实现任何接口,但需要提供“getValue()”和“setValue()”(val属性不需要,var需要)函数。

         如:

class Delegate {  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.'")  }}


         当读取“p”的值时,实际是访问它的代理“Delegate”实例的“getValue()”函数,第一个参数是读取“p”的实例(即对应的类的实例),第二个参数为“p”自身的描述。如:
val e = Example()println(e.p)  //Example@33a17727, thank you for delegating ‘p’ to me!

         同样,当给“p”赋值时,会调用代理的“setValue()”函数;前面两个参数跟“getValue()”函数一致,第三个参数为赋的值。如:

e.p = "NEW"//打印结果://NEW has been assigned to ‘p’ in Example@33a17727.

 

      属性代理的要求(Property Delegate Requirements

         下面总结代理属性的要求:

         只读属性(read-only,使用val定义)

         代理类提供“getValue”函数,参数要求:

         Ø  接收者(receiver):第一个参数,必须是属性对应的类或父类型。

         Ø  元数据(metadata):第二个参数,必须是“KProperty<*>”或它的父类型。

         Ø  该函数,必须返回一个跟属性同类型的值。

 

         可变属性(mutable,使用var定义)

         代理类的“getValue”函数跟只读属性的一样;另外还需要提供一个“setValue”函数,参数要求:

         Ø  接收者(receiver):第一个参数,同“getValue”对应的参数。

         Ø  元数据(metadata):第二个参数,同“getValue”对应的参数。

         Ø  新值:第三个参数,类型必须跟属性一样或其父类型。

 

         “getValue”与“setValue”函数,可以是代理类的成员函数或扩展函数。当需要将一个对象作为一个代理属性的代理,而该类没有对应的“getValue”与“setValue”函数,通过扩展函数方式实现就非常方便。

         另外两个函数需要使用“operator”关键字修饰。

 

         标准代理(Standard Delegates)

         Kotlin的标准库,通过工厂方法提供了一些有用的代理类。

         Lazy

         “lazy()”函数接受Lambda表达式 并 返回“Lazy<T>”实例,它可以当做延迟属性的代理实现:当第一次属性执行“get()”(获取属性值,即使用该属性)时,会通过“lazy()”函数执行添加的Lambda表达式并记录返回值;后续再使用该属性时,直接使用记录的值。
val lazyValue: String by lazy {    println("computed!")    "Hello"}fun main(args: Array<String>) {    println(lazyValue)    println(lazyValue)}//prints://computed!//Hello//Hello


         默认情况,延迟属性的赋值是线程同步的:只会在一个线程中计算一次值,其他线程使用同一个值。如果初始化同步不是必须的,可以通过将“lazy()”函数的参数设置为“LazyThreadSafetyMode.PUBLICATION”,那么多个线程对其同时赋值。如果可以确保属性只会在单个线程中初始化,可以将“lazy()”设置“LazyThreadSafetyMode.NONE”模式,该模式下,不会确保线程安全及相关开销。

 

         可观察方法(Observable)

         “Delegates.observable()”,包含两个参数:初始化值和 属性值修改的回调handler;每次对属性赋值操作,都会回调该handler方法(在属性赋值后执行),该方法包含三个参数,分别为:属性对象,原值,新值。

class User {    var name: String by Delegates.observable("<nomalName>") {        prop, old, new ->        println("$old -> $new")    }}fun main(args: Array<String>) {    val user = User()    user.name = "first"    user.name = "second"}//结果://< nomalName > -> first//first -> second

         另,如果需要拦截修改属性值动作并禁止修改,可以使用“Delegates.vetoable()”,参数跟“observable()”类似,第二个回调handler需要返回一个Boolean,true表示同意修改,false表示禁止修改;该回调会在属性值修改前调用。

class User {    var name: String by Delegates. vetoable ("<nomalName>") {        prop, old, new ->        println("want modify $old -> $new")        false}}fun main(args: Array<String>) {val user = User()println(user.name)user.name = "newValue"println(user.name)}//结果://< nomalName >//want modify < nomalName > -> newValue//< nomalName >

         Storing Properties in a Map

         经常会在map中存储属性值,经常用于如解析JSON或其他“动态”事情。可以使用map对象作为一个代理属性的代理:
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。




0 0
原创粉丝点击