Kotlin - by 关键字介绍

概述

Kotlin 中 by 关键字用来简化实现代理 (委托) 模式,不仅可以类代理,还可以代理类属性, 监听属性变化,下面我们来介绍by的几种主要使用场景:

  • 类的代理 class
  • 属性延迟加载 lazy
  • 监听属性变化 Delegates.observable ( 扩展 Delegates.vetoable )
  • 自定义监听属性变化 ReadWriteProperty
  • 属性非空强校验 Delegates.notNull()
  • Map值 映射到类属性 map

类的代理(代理/委托模式)

// 定义一个接口,和一个方法 show()
interface Base {  
    fun show()
}

// 定义类实现 Base 接口, 并实现 show 方法
open class BaseImpl : Base {  
    override fun show() {
        print("BaseImpl::show()")
    }
}

// 定义代理类实现 Base 接口, 构造函数参数是一个 Base 对象
// by 后跟 Base 对象, 不需要再实现 show() 
class BaseProxy(base: Base) : Base by base

// main 方法 
fun main(args: Array<String>) {  
    val base = BaseImpl()
    BaseProxy(base).show()
}

转成 Java 代码

// Base.java

public interface Base {  
   void show();
}


// BaseImpl.java

public class BaseImpl implements Base {  
   public void show() {
      String var1 = "BaseImpl::show()";
      System.out.print(var1);
   }
}


// BaseProxy.java

public final class BaseProxy implements Base {  
   // $FF: synthetic field
   private final Base $$delegate_0;

   public BaseProxy(@NotNull Base base) {
      Intrinsics.checkParameterIsNotNull(base, "base");
      super();
      this.$$delegate_0 = base;
   }

   public void show() {
      this.$$delegate_0.show();
   }
}
// NormalKt.java

public final class NormalKt {  
   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      BaseImpl base = new BaseImpl();
      (new BaseProxy((Base)base)).show();
   }
}

和一般的代理模式是不是一样?不过 by 关键字节省了不少代码

属性延迟加载

// lzay 后跟表达式,表达式返回值必须和属性类型一致
class LazySample {  
    val lazy: String by lazy {
        println("init!")
        "my lazy"
    }
}

fun main(args: Array<String>) {  
    val sample = LazySample()
    println("lazy = ${sample.lazy}")
    println("lazy = ${sample.lazy}")
}

输出结果:

init!
lazy = my lazy
lazy = my lazy

代码分析

// 一个函数,参数为返回 T 的表达式,返回结果类型为Lazy<T>,返回结果具体由 SynchronizedLazyImpl 这个类实现 
@kotlin.jvm.JvmVersion
public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

// 理解 = 是函数直接实现
fun max(a: Int, b: Int) = if (a > b) a else b

// 上述的返回值类型 Lazy 接口
public interface Lazy<out T> {  
    public val value: T
    public fun isInitialized(): Boolean
}
// out 表示类型为T或T的子类, 而且类是只读

// SynchronizedLazyImpl 类, 两个参数,一个表达式,第二个 默认为空的 Object, 对象继承 Lazy, Serializable
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
    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
}

// lazy 方法返回一个 SynchronizedLazyImpl 对象
var lazyObject = lazy {  
    println("init")
    "xxxx"
}
println("#=" + lazyObject.value)  

转化为 Java

public final class LazySample {  
   @NotNull
   private final Lazy lazyObject$delegate;
   // $FF: synthetic field
   static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(LazySample.class), "lazyObject", "getLazyObject()Ljava/lang/String;"))};

   @NotNull
   public final String getLazyObject() {
      Lazy var1 = this.lazyObject$delegate;
      return (String)var1.getValue();
   }

   public LazySample() {
      this.lazyObject$delegate = LazyKt.lazy((Function0)null.INSTANCE);
   }
}

监听属性变化

import kotlin.properties.Delegates

class User2 {  
    var name: String by Delegates.observable("oldName") {
        kProperty, old, new ->
        println("${kProperty.returnType}, $old -> $new")
    }

    // 默认值不受 vetoable 影响
    var address: String by Delegates.vetoable("wan", {
        kProperty, oldValue, newValue ->
        println("oldValue:$oldValue | newValue:$newValue")
        newValue.contains("wang")
    })
}

fun main(args: Array<String>) {  
    val user = User2()
    println(user.name)
    user.name = "Carl"

    user.address = "abcd";
    println(user.address)
}

源码分析

// observable方法
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)
    }

// vetoable 方法
public inline fun <T> vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean):  
    ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) {
        override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue)
    }


public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {  
    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: T, newValue: T): 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: 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
        if (!beforeChange(property, oldValue, value)) {
            return
        }
        this.value = value
        afterChange(property, oldValue, value)
    }
}

自定义属性变化

class Example {  
    var p: String by Delegate()
    var w: String? by A()
    val x:String? by B()
}

class Delegate() {  
    // 运算符重载
    operator fun getValue(thisRef: Any?, prop: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${prop.name}' to me!"
    }

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


class A : ReadWriteProperty<Any?, String?> {  
    override fun getValue(thisRef: Any?, property: KProperty<*>): String? {
        return "aaaa"
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: String?) {
        println("setValue=" + value)
    }

}

class B : ReadOnlyProperty<Any?, String?> {  
    override fun getValue(thisRef: Any?, property: KProperty<*>): String? {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
}


// ReadWriteProperty 定义
public interface ReadWriteProperty<in R, T> {  
    /**
     * 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 operator fun getValue(thisRef: R, property: KProperty<*>): T

    /**
     * Sets 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.
     * @param value the value to set.
     */
    public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}

属性非空强校验

class User {  
    var name: String by Delegates.notNull()

    fun init(name: String) {
        this.name = name
    }
}

fun main(args: Array<String>) {  
    val user = User()
    // print(user.name)
    // user.name -> IllegalStateException
    user.init("Carl")
    println(user.name)
}

源码解析

// notNull 方法
public fun <T: Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()

// 具体实现方法 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
    }
}

Map值 映射到类属性

class UserX(val map: Map<String, Any?>) {  
    val name: String by map
    val age: Int     by map
}

fun main(args: Array<String>) {  
    val user = UserX(mapOf(
            "name" to "John Doe",
            "age"  to 123
    ))

    // key 不存在报错  Key age is missing in the map.
    // 类型不一致 java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number


    println("name = ${user.name}, age = ${user.age}")
}

转化为 Java代码

// UserX.java
package delegate;

import java.util.Map;  
import kotlin.Metadata;  
import kotlin.collections.MapsKt;  
import kotlin.jvm.internal.Intrinsics;  
import kotlin.jvm.internal.PropertyReference1Impl;  
import kotlin.jvm.internal.Reflection;  
import kotlin.reflect.KProperty;  
import org.jetbrains.annotations.NotNull;

public final class UserX {  
   @NotNull
   private final Map name$delegate;
   @NotNull
   private final Map age$delegate;
   @NotNull
   private final Map map;
   // $FF: synthetic field
   static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(UserX.class), "name", "getName()Ljava/lang/String;")), (KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(UserX.class), "age", "getAge()I"))};

   @NotNull
   public final String getName() {
      Map var1 = this.name$delegate;
      KProperty var3 = $$delegatedProperties[0];
      return (String)MapsKt.getOrImplicitDefaultNullable(var1, var3.getName());
   }

   public final int getAge() {
      Map var1 = this.age$delegate;
      KProperty var3 = $$delegatedProperties[1];
      return ((Number)MapsKt.getOrImplicitDefaultNullable(var1, var3.getName())).intValue();
   }

   @NotNull
   public final Map getMap() {
      return this.map;
   }

   public UserX(@NotNull Map map) {
      Intrinsics.checkParameterIsNotNull(map, "map");
      super();
      this.map = map;
      this.name$delegate = this.map;
      this.age$delegate = this.map;
   }
}

参考文档:

官方文档:https://try.kotlinlang.org/#/Examples/Delegated%20properties/Custom%20delegate/ kotlin泛型out和in介绍:http://www.jianshu.com/p/488fb964b50a