美文网首页Kotlin探索Android开发Kotlin
Kotlin中理解委托属性,并自定义委托

Kotlin中理解委托属性,并自定义委托

作者: 小吉快跑呀 | 来源:发表于2017-11-19 17:17 被阅读128次

    常用的by lazy 延迟委托 , by Delegates.observable() 可观察属性委托, by Delegates.nonNull()等等

    语法是: val/var<属性名>:<类型> by<表达式>。在 by后面的表达式是该委托

    属性对应的get() (和set())会被委托给它的getValue()和setValue()方法。
    所以,kotlin中的代理仅仅是代理了get 和 set 两个方法

    属性的委托不必实现任何的接口,但是需要重载操作符getValue()函数(和setValue()——对于var属性)
    但是对于val可以实现ReadOnlyProperty,var实现ReadWriteProperty接口(就是帮你实现两个需要重载的操作符的接口)来更快的进行自定义委托.

    //此段代码为ReadWriteProperty的接口
    public interface ReadWriteProperty<in R, T> {
        public operator fun getValue(thisRef: R, property: KProperty<*>): T
        public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
    }
    

    那么他们究竟是怎么工作的呢?
    我们来看看nonNull()委托的代码吧
    我初始化了一个Int对象,且用委托表示int1不可以为空
    var int1 : Int by Delegates.notNull()
    那么我们进入notNull()的代码里查看他的实现
    public fun <T: Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()
    很明显,代理给一个ReadWriteProperty类了
    那么NotNullVar()是什么呢

    private class NotNullVar<T: Any>() : ReadWriteProperty<Any?, T> {
        private var value: T? = null
    
        public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
        }
    
        public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            this.value = value
        }
    }
    

    我来解释一下,当给int1复制的时候,就会自动调用上面的setValue(),其中的三个参数
    第一个thisRef表示持有该对象的对象,即该int1的所有者.
    第二个参数 property 是该值的类型,
    第三个参数 value 就是属性的值了
    在第一次调用setvalue的时候,将该值存到value中,getvalue的时候对value进行是否为空的判断.空就抛异常,这就完成了nonNull的委托


    类似的我们看看by Delegates.observable() 这个委托
    先看看该委托的使用方法吧

        var int2 : Int by Delegates.observable(1){
            property, oldValue, newValue ->
            //oldvalue是修改前的值,newValue是修改后的值
        }
    

    该委托的实现

        public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
            ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) {
                override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
            }
    
    

    最终委托到的对象,注意这是一个抽象类

    public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {
        private var value = initialValue
    
        protected open fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = true
    
    
        protected open fun afterChange (property: KProperty<*>, oldValue: T, newValue: T): Unit {}
    
        public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return value
        }
    
        public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            val oldValue = this.value //修改前的值
    
            //beforeChange一直返回true 不知此段代码意义何在
            if (!beforeChange(property, oldValue, value)) {
                return
            }
            this.value = value //修改后的值
            //调用上一段代码中重写的方法
            afterChange(property, oldValue, value)
        }
    }
    
    

    很明显,在setValue的过程中,调用了afterChange(),而afterChange是你使用该代理的时候传入的lambda,所以每次修改该对象的值的时候,都会调用你传入的函数,实现了对对象的值改变的观察.


    看完上面两个例子,我们来自定义一个委托用来实现finViewById吧
    当然例子很简陋,只能在activity中使用,仅供参考

    
    class MainActivity : AppCompatActivity() ,MainContract.View {
    
        val imageView : ImageView by bindView(R.id.imageView)
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            imageView.setImageDrawable(null)
        }
    }
    
    fun <T: View> bindView( id : Int): FindView<T> = FindView(id)
    
    class FindView<T : View >(val id:Int) : ReadOnlyProperty<Any?,T>{
    
        override fun getValue(thisRef: Any?, property: KProperty<*>): T {
    
            if(this.value == null) {
                this.value = (thisRef as Activity).findViewById<T>(id)
            }
            return this.value?:throw RuntimeException("can not find this view")
        }
    
        var value : T? = null
    }
    

    但是,为什么thisref 可以强转成activity呢?上面已经说了thisRef是该对象的持有者,在上面的代码中,即MainActivity.

    我们将kotlin代码转成字节码再转到java代码中查看

    
    //反射得到该类的属性
    static final KProperty[] $$delegatedProperties = 
    new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(
            Reflection.getOrCreateKotlinClass(MainActivity.class), 
                    "imageView", "getImageView()Landroid/widget/ImageView;"))};
    
      //真正的代理类
       @NotNull
       private final FindView imageView$delegate = MainActivityKt.bindView(2131230801);
    
    
      //通过代理类得到imageview
       @NotNull
       public final ImageView getImageView() {
          //在这里,我们就一清二楚的知道了getValue传入的两个参数究竟是什么了!!
          return (ImageView)this.imageView$delegate.getValue(this, $$delegatedProperties[0]);
       }
    
       protected void onCreate(@Nullable Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          this.setContentView(2131361819);
          //通过代理类得到imageview
          this.getImageView().setImageDrawable((Drawable)null);
       }
    

    代理类的实现

    public final class FindView implements ReadOnlyProperty {
       @Nullable
       private View value;
       private final int id;
    
       @NotNull
       public View getValue(@Nullable Object thisRef, @NotNull KProperty property) {
          Intrinsics.checkParameterIsNotNull(property, "property");
          if(this.value == null) {
             if(thisRef == null) {
                throw new TypeCastException("null cannot be cast to non-null type android.app.Activity");
             }
    
             this.value = ((Activity)thisRef).findViewById(this.id);
          }
    
          View var10000 = this.value;
          if(this.value != null) {
             return var10000;
          } else {
             throw (Throwable)(new RuntimeException("can not find this view"));
          }
       }
    
       // $FF: synthetic method
       // $FF: bridge method
       public Object getValue(Object var1, KProperty var2) {
          return this.getValue(var1, var2);
       }
    
       @Nullable
       public final View getValue() {
          return this.value;
       }
    
       public final void setValue(@Nullable View var1) {
          this.value = var1;
       }
    
       public final int getId() {
          return this.id;
       }
    
       public FindView(int id) {
          this.id = id;
       }
    }
    

    相关文章

      网友评论

        本文标题:Kotlin中理解委托属性,并自定义委托

        本文链接:https://www.haomeiwen.com/subject/sztpvxtx.html