Kotlin委托的使用和原理

Kotlin委托的使用和原理

什么是委托

委托就是一种通过将实际工作交给其他对象来实现的设计模式;kotlin给我们提供了by关键词实现委托.

类委托

首先来看类委托,类委托的意思就是A中持有一个B的实例,A实现某个功能都交给B来实际执行.我们来看代码:

kotlin 复制代码
interface IVehicle {
    fun drive():String
}
​
class Car(private val vehicle: IVehicle) : IVehicle by vehicle
​
fun main(){
    val car = Car(object :IVehicle{
        override fun drive(): String {
            return "drive car"
        }
    })
    println(car.drive())
}

Car类实现了IVehicle接口及持有了一个IVehicle实例,并通过by关键词将逻辑委托给vehicle.反编译成java代码看下:

kotlin 复制代码
public final class Car implements IVehicle {
   private final IVehicle vehicle;
​
   public Car(@NotNull IVehicle vehicle) {
      Intrinsics.checkNotNullParameter(vehicle, "vehicle");
      super();
      this.vehicle = vehicle;
   }
​
   @NotNull
   public String drive() {
      return this.vehicle.drive();
   }
}

Car这个类实现了IVehicle,且通过by属性委托,编译器自动生成了drive()接口方法的实现,并调用构造方法中传入的委托对象的drive()方法.

属性委托

属性委托就是指一个类实现了ReadWriteProperty接口,同时把一个属性的get和set方法委托给这个委托类.我们来看代码:

kotlin 复制代码
class PropertyDelegate: ReadWriteProperty<Any?, Int> {
    var v = 0
    override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
        println("getValue")
        return v
    }
​
    override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
        println("setValue")
        v = value * value
    }
}
​
fun main(){
    var propertyValue :Int by PropertyDelegate()
    propertyValue = 10
    println(propertyValue)
}

这段代码中首先声明了一个委托类,实现了setValue和getValue两个方法,实际上这两个方法都是运算符重载方法,以getValue为例,它其实是这个方法:

kotlin 复制代码
/**
 * Returns the value of the property for the given object.
 * @param thisRef the object for which the value is requested.
 * @param property the metadata for the property.
 * @return the property value.
 */
public override operator fun getValue(thisRef: T, property: KProperty<*>): V

在main方法中通过by关键字把value委托给委托类实例,只要对value赋值或获取value时,就会调用到委托类的getValue和setValue方法.

我们看下反编译后的代码:

typescript 复制代码
public final class PropertyDelegate implements ReadWriteProperty {
   private int v;
​
   public final int getV() {
      return this.v;
   }
​
   public final void setV(int var1) {
      this.v = var1;
   }
​
   @NotNull
   public Integer getValue(@Nullable Object thisRef, @NotNull KProperty property) {
      Intrinsics.checkNotNullParameter(property, "property");
      String var3 = "getValue";
      System.out.println(var3);
      return this.v;
   }
​
   // $FF: synthetic method
   // $FF: bridge method
   public Object getValue(Object var1, KProperty var2) {
      return this.getValue(var1, var2);
   }
​
   public void setValue(@Nullable Object thisRef, @NotNull KProperty property, int value) {
      Intrinsics.checkNotNullParameter(property, "property");
      String var4 = "setValue";
      System.out.println(var4);
      this.v = value * value;
   }
​
   // $FF: synthetic method
   // $FF: bridge method
   public void setValue(Object var1, KProperty var2, Object var3) {
      this.setValue(var1, var2, ((Number)var3).intValue());
   }
}
​
public final class PropertyDelegateKt {
   // $FF: synthetic field
   static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty0(new MutablePropertyReference0Impl(PropertyDelegateKt.class, "propertyValue", "<v#0>", 1))};
​
   public static final void main() {
      PropertyDelegate var10000 = new PropertyDelegate();
      KProperty var1 = $$delegatedProperties[0];
      PropertyDelegate propertyValue = var10000;
      propertyValue.setValue((Object)null, var1, 10);
      int var2 = propertyValue.getValue((Object)null, var1);
      System.out.println(var2);
   }
​
   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}
​
​

在main方法中,生成了一个PropertyDelegate代理类,当对propertyValue赋值时就会调用代理类的setValue方法,当获取propertyValue值的时候就调用代理类的getValue方法.

属性委托还有一种用法,看一下代码

kotlin 复制代码
fun main(){
    val map:MutableMap<String,String> = mutableMapOf()
    map["name"] = "name1"
​
    var name by map
    println(name)
    name = "name2"
}

当访问/修改name时,就会从map中获取key为name的value值并返回,看下反编译后的代码:

arduino 复制代码
public final class MapDelegateKt {
   // $FF: synthetic field
   static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty0(new MutablePropertyReference0Impl(MapDelegateKt.class, "name", "<v#0>", 1))};
​
   public static final void main() {
      Map map = (Map)(new LinkedHashMap());
      map.put("name", "name1");
      KProperty var2 = $$delegatedProperties[0];
      Object var4 = null;
      Object var3 = MapsKt.getOrImplicitDefaultNullable(map, var2.getName());
      System.out.println(var3);
      var4 = null;
      String var5 = "name2";
      map.put(var2.getName(), var5);
   }
​
   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}
  1. 通过反射生成了一个KProperty类型对象变量$$delegatedProperties,通过这个对象的getName()我们就能拿到变量名称,比如这里的"name"变量名;
  2. 最终调用了MapsKt.getOrImplicitDefaultNullable方法,去map散列表去查找"name"这个key对应的value;

Kotlin提供的属性委托

  1. 延迟属性Lazy

    kotlin 复制代码
    val count : Int by lazy{
        println("init")
        0
    }

    lazy() 接受一个lambda表达式并返回一个Lazy实例的函数,只有当第一次调用count时,count变量才会初始化,也就是调用lambda表达式.lazy()的实现是一个SynchronizedLazyImpl()实例

    kotlin 复制代码
    private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
        private var initializer: (() -> T)? = initializer
        @Volatile private var _value: Any? = UNINITIALIZED_VALUE
        // final field is required to enable safe publication of constructed instance
        private val lock = lock ?: this
    
        override val value: T
            get() {
                val _v1 = _value
                if (_v1 !== UNINITIALIZED_VALUE) {
                    @Suppress("UNCHECKED_CAST")
                    return _v1 as T
                }
    
                return synchronized(lock) {
                    val _v2 = _value
                    if (_v2 !== UNINITIALIZED_VALUE) {
                        @Suppress("UNCHECKED_CAST") (_v2 as T)
                    } else {
                        val typedValue = initializer!!()
                        _value = typedValue
                        initializer = null
                        typedValue
                    }
                }
            }
    
        override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
    
        override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
    
        private fun writeReplace(): Any = InitializedLazyImpl(value)
    }
  2. 可观察属性Observable

    kotlin 复制代码
    var age : Int by Delegates.observable(0) {
            _, oldValue, newValue ->
        println("old value : $oldValue, new value : $newValue")
        newValue > oldValue
    }

    Delegates.observable()接受两个参数,一个是初始值,一个是修改时的lambda表达式:

    kotlin 复制代码
    public abstract class ObservableProperty<V>(initialValue: V) : ReadWriteProperty<Any?, V> {
        private var value = initialValue
    
        /**
         *  The callback which is called before a change to the property value is attempted.
         *  The value of the property hasn't been changed yet, when this callback is invoked.
         *  If the callback returns `true` the value of the property is being set to the new value,
         *  and if the callback returns `false` the new value is discarded and the property remains its old value.
         */
        protected open fun beforeChange(property: KProperty<*>, oldValue: V, newValue: V): Boolean = true
    
        /**
         * The callback which is called after the change of the property is made. The value of the property
         * has already been changed when this callback is invoked.
         */
        protected open fun afterChange(property: KProperty<*>, oldValue: V, newValue: V): Unit {}
    
        public override fun getValue(thisRef: Any?, property: KProperty<*>): V {
            return value
        }
    
        public override fun setValue(thisRef: Any?, property: KProperty<*>, value: V) {
            val oldValue = this.value
            if (!beforeChange(property, oldValue, value)) {
                return
            }
            this.value = value
            afterChange(property, oldValue, value)
        }
    }
相关推荐
严文文-Chris1 小时前
【设计模式-享元】
android·java·设计模式
丶白泽2 小时前
重修设计模式-设计原则
设计模式·接口隔离原则·依赖倒置原则·开闭原则
【D'accumulation】2 小时前
典型的MVC设计模式:使用JSP和JavaBean相结合的方式来动态生成网页内容典型的MVC设计模式
java·设计模式·mvc
仙魁XAN4 小时前
Unity 设计模式 之 创造型模式-【工厂方法模式】【抽象工厂模式】
unity·设计模式·工厂方法模式·抽象工厂模式
龙哥·三年风水14 小时前
活动系统开发之采用设计模式与非设计模式的区别-后台功能总结
设计模式·php·tinkphp6
一头老羊14 小时前
前端常用的设计模式
设计模式
极客先躯15 小时前
java和kotlin 可以同时运行吗
android·java·开发语言·kotlin·同时运行
严文文-Chris15 小时前
【设计模式-组合】
设计模式
kimloner16 小时前
工厂模式(二):工厂方法模式
java·设计模式·工厂方法模式
丶白泽19 小时前
重修设计模式-结构型-桥接模式
java·设计模式·桥接模式