Kotlin的by 托付

1. by lazy的原理解析

咱们用kotlin经常会用到by lazy,所以我之前一直认为这俩是必须一重用的,但其实bylazy是拆开的,像下面这段代码:

class By {
    val tag by lazy {
        "hello"
    }
}

能够依照下面的格式来理解上面的代码

val / var <property name>: <Type> by <delegate>

kotlin转成java之后的代码如下:

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 1,
   d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002 ... "},
   d2 = {"Ldelegate/By;", "", "()V", "tag", "", "getTag", "()Ljava/lang/String;", "tag$delegate", "Lkotlin/Lazy;", "TestKotlin"}
)
public final class By {
   static final KProperty[] $$delegatedProperties = ...
   @NotNull
   private final Lazy tag$delegate;
   @NotNull
   public final String getTag() {
      Lazy var1 = this.tag$delegate;
      KProperty var3 = $$delegatedProperties[0];
      return (String)var1.getValue();
   }
   public By() {
      this.tag$delegate = LazyKt.lazy((Function0)null.INSTANCE);
   }
}
  • tag$delegate特点后面有后缀$delegate
  • 留意tag$delegate的类型是Lazy,而不是String
  • By构造办法里面把LazyKt.lazy赋值tag$delegate
  • 所以getTag回来值为给定的代码块的履行成果

感觉仍是不是很清晰,持续从Kotlin源码视点看Lazy的完成逻辑:

public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
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)
}

完成逻辑如下:

  1. 默认为SynchronizedLazyImpl完成,initializer是lambda表达式,比方最上面的Lazy的lambda为{"hello"}
  2. 默认值赋值为UNINITIALIZED_VALUE,如果不为默认值则直接回来,说明现已赋值过了。
  3. 后面的赋值办法由Synchronized包裹,支撑多线程拜访,先查看是否为UNINITIALIZED_VALUE,不为默认值直接回来
  4. 未赋值过,先履行initializer,回来值赋值给typedValue,一起initializer = null,然后回来成果value

这儿涉及到的几个知识点,简略介绍一下:

1.1. 怎么查看Kotlin生成的java代码

Kotlin的by 委托

1.2. 这儿的Metadata是干啥的

每个kotlin生成java的类里面都会有这样的一个Metadata的注解

@Metadata(
   ...
)

kotlin官网对此有介绍,Metadata

This annotation is present on any class file produced by the Kotlin compiler and is read by the compiler and reflection. Parameters have very short JVM names on purpose: these names appear in all generated class files, and we’d like to reduce their size.

1.3. lambda表达式

lambda表达式是Kotlin的很重要的一个内容,简略介绍一下上面源码中涉及到的内容,抛砖引玉,有爱好能够详细了解。
上面的源码顶用到了这么一句:

fun <T> lazy(initializer: () -> T)

标明传入了一个lambda表达式,需求无参并回来T。下面列出一些常用的简略lambda函数类型

//无参、无回来值的函数类型(Unit 回来类型不行省略)
() -> Unit
//接纳T类型参数、无回来值的函数类型
(T) -> Unit
//接纳T类型和A类型参数、无回来值的函数类型(多个参数同理)
(T,A) -> Unit
//接纳T类型参数,而且回来R类型值的函数类型
(T) -> R
//接纳T类型和A类型参数、而且回来R类型值的函数类型(多个参数同理)
(T,A) -> R

怎么得到lambda的成果呢?上面的代码

val typedValue = initializer!!()

运用了()得到履行的成果,也能够运用initializer!!.invoke()得到履行成果。

2. by – 托付

Kotlin 直接支撑托付形式,愈加优雅,简练,经过关键字 by 完成托付。kotlin中的托付大致有下面几种形式:

类托付

类的托付即一个类中界说的办法实践是调用另一个类的目标的办法来完成的。

// 界说一个接口
interface Base {
    fun print()
}
// 完成这个接口
class RealImpl(val x: Int) : Base {
    override fun print() {
        print(x)
    }
}
// 完成类托付
class Delegate(b: Base) : Base by b

上面是最简略的类托付的完成:

  1. 界说一个接口Base
  2. 创立一个类RealImpl完成接口
  3. 创立一个类Delegate,运用关键字by,标明Deledate相关的特点办法都托付传入的b来完成

看起来迷迷糊糊的,咱们看一下Delegate的java代码完成:

public final class Delegate implements Base {
   // $FF: synthetic field
   private final Base $$delegate_0;
   public Delegate(@NotNull Base b) {
      Intrinsics.checkParameterIsNotNull(b, "b");
      super();
      this.$$delegate_0 = b;
   }
   public void print() {
      this.$$delegate_0.print();
   }
}

一望而知,传入的b赋值给$$delegate_0,Delegate类完全是调用了$$delegate_0的办法来完成自己的办法。

特点托付

特点托付指的是一个类的某个特点值不是在类中直接进行界说,而是将其托付给一个代理类,从而完成对该类的特点统一管理。

val/var <特点名>: <类型> by <表达式>

by 关键字之后的表达式便是托付, 特点的 get() 办法(以及set() 办法)将被托付给这个目标的 getValue() 和 setValue() 办法。特点托付不必完成任何接口, 但必须提供 getValue() 函数(关于 var特点,还需求 setValue() 函数)。该类需求包括 getValue() 办法和 setValue() 办法,且参数 thisRef 为进行托付的类的目标,prop 为进行托付的特点的目标。

import kotlin.reflect.KProperty
// 界说包括特点托付的类
class Example {
    var p: String by Delegate()
}
// 托付的类
class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, 这儿托付了 ${property.name} 特点"
    }
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$thisRef${property.name} 特点赋值为 $value")
    }
}
fun main(args: Array<String>) {
    val e = Example()
    println(e.p)     // 拜访该特点,调用 getValue() 函数
    e.p = "Runoob"   // 调用 setValue() 函数
    println(e.p)
}
//Example@433c675d, 这儿托付了 p 特点
//Example@433c675d 的 p 特点赋值为 Runoob
//Example@433c675d, 这儿托付了 p 特点

需求留意的是,getValue和setValue的参数是固定的。

规范托付
1. lazy 上面介绍过了,越过
2. NotNull

notNull 适用于那些无法在初始化阶段就确定特点值的场合,如果特点在赋值前就被拜访的话则会抛出反常。
用法如下:

class NotNull {
    class User {
        val id: Int by Delegates.notNull<Int>()
    }
}

反编译成java代码:

   public static final class User {
      // $FF: synthetic field
      static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(NotNull.User.class), "id", "getId()I"))};
      @org.jetbrains.annotations.NotNull
      private final ReadWriteProperty id$delegate;
      public final int getId() {
         return ((Number)this.id$delegate.getValue(this, $$delegatedProperties[0])).intValue();
      }
      public User() {
         this.id$delegate = Delegates.INSTANCE.notNull();
      }
   }
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
   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
    }
}

源码简略,便是特点托付,在没赋值的时分getValue会抛出反常。

3. Observable

observable 能够用于完成调查者形式,每次改动都会回调。

    class Time {
        var day: Int by Delegates.observable(0, { property: KProperty<*>, oldValue: Int, newValue: Int ->
            println("change before: $oldValue, after: $newValue")
        })
    }
4. Vetoable

vetoable与 observable一样,能够调查特点值的变化,不同的是,vetoable能够经过处理器函数来决议特点值是否生效。

    class User {
        var id: Int by Delegates.vetoable(10, { property: KProperty<*>, oldValue: Int, newValue: Int ->
            // only setValue work when newValue bigger than oldValue
            newValue > oldValue
        })
    }

上面的代码表示赋值的时分只要newValueoldValue大的时分才会赋值成功。

源码上ObservableVetoable相似,

    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 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)
        }

这两个调用相似,传入初始值和lambda,回来了一个ObservableProperty的匿名内部类,不同的是重写的办法不一样。
observable重写了afterChangevetoable重写了beforeChange

持续看ObservableProperty的源码:

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
        if (!beforeChange(property, oldValue, value)) {
            return
        }
        this.value = value
        afterChange(property, oldValue, value)
    }
}

能够看到其实vetoable便是用beforeChange做了一个写特点的拦截。observable便是做了一个afterChange的回调。