目录

Kotlin系列(二十三):秘技之委托属性


一、前言:

  • 虽然之前了解Kotlin原生支持的委托模式特性时比较尴尬,但是其提供的委托属性确实及其强大及好用的一个特性。
  • 下面我们将通过分析kotlin的委托属性及其对应的java代码来更加深入地学习和理解Kotlin所提供的特性的内部原理。

二、委托属性

  • 有一些常见的属性类型,虽然我们可以在每次需要的时候手动实现它们,但是如果能够为大家把他们只实现一次并放入一个库会更好。
  • 例如包括:
  • 1、延迟属性(lazy properties): 其值只在首次访问时计算,在Android等移动端领域使用能极好的控制启动速度,不是必要的属性可以延迟加载;
  • 2、可观察属性(observable properties): 监听器会收到有关此属性变更的通知;
  • 3、把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中,属性过多时管理方便。
  • 为了涵盖这些(以及其他)情况,Kotlin 支持 委托属性:

委托属性代码的学习

  • 以下我们便通过源代码来学习其委托属性

  • 1、创建一个Boss类,其工作需要秘书进行代理完成,因为我是老板嘛……( ̄∇ ̄)

      package com.anjie.demo
    
      /**
       * Created by 安杰 on 2017/11/2. */
       class Boss {
      		var workd: String by Secretary()
      }
    
  • 可以看到,语法很简单,我们只需要记住by关键字即可

  • 然后我们声明一个秘书类Secretary

      package com.anjie.demo
    
      import kotlin.reflect.KProperty
    
      /**
       * Created by 安杰 on 2017/11/2. */
       class Secretary {
    
      		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.'")
      		}
      }
    
  • 如上,其代理原理为:

  • 1、被代理字段workd的setter()会委托给代理类的setValue()方法;
  • 2、被代理字段workd的getter()会委托给代理类的getValue()方法;
  • 所以设置了代理类之后,当被代理的字段发生变化时,我们便可在代理类里面进行接收和处理。
  • 测试代码如下:

      @Test
      fun test1() {
      		val b = Boss()
      		b.workd="sleep"
      	println(b.workd)
      }
    
  • 结果输出如下:

      >sleep has been assigned to 'workd in com.anjie.demo.Boss@5ccd43c2.'
      com.anjie.demo.Boss@5ccd43c2, thank you for delegating 'workd' to me!
    
  • 代码解析:

  • 可以看到thisRef==被代理的类,property存储了被代理的属性,property.name则为属性的名称;value为给被代理字段赋值时传递的值。

  • 我的天,怎么那么厉害?其实内部原理很简单,只是kotlin帮我们做了点事而已,我们看下Boss类对应的java代码:

      public final class Boss {
      	static final KProperty[] $delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Boss.class), "workd", "getWorkd()Ljava/lang/String;"))};
    
      	 @NotNull
      	private final Secretary workd$delegate = new Secretary();
    
      	 @NotNull
      	public final String getWorkd() {
      			return this.workd$delegate.getValue(this, $delegatedProperties[0]);
      	 }
    
      	 public final void setWorkd(@NotNull String var1) {
      			Intrinsics.checkParameterIsNotNull(var1, "");
      			this.workd$delegate.setValue(this, $delegatedProperties[0], var1);
      	 }
      }
    
  • 代码解析:

  • 可以看到,当粗案件Boss时,内部生成类一个代理成员-Secretary workd$delegate;

  • 当我们访问被代理字段workd时,其setter和getter会被调用,而Setter和getter里面再调用了代理类对应的方法,并将值传进入,如此我们的代理对象便能接收到了。

  • OK,相信看了上面的代码的你将会觉得Kotlin的委托属性不再神秘!

  • 注意:因为设置了代理后,setter和getter需要kotlin来生成,所以我们就不能够再重写被代理属性的setter和getter了。

三、总结:

  • 通过分析Kotlin对应Java代码,我们很好的学习了Kotlin中代理属性的使用及其原理。
  • 相信看完本章后,对于Kotlin的委托属性的使用对于你来说将是非常简单的。