Kotlin by lazy的原理解析委托及类委托

2023-06-1419:57:55编程语言入门到精通Comments860 views字数 6957阅读模式

1. by lazy的原理解析

我们用kotlin经常会用到by lazy,所以我之前一直以为这俩是必须一起用的,但其实bylazy是拆开的,像下面这段代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

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

可以按照下面的格式来理解上面的代码文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

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

kotlin转成java之后的代码如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 1,
   d1 = {"u0000u0014nu0002u0018u0002nu0002 ... "},
   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的实现逻辑:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

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

实现逻辑如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

  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 lazy的原理解析委托及类委托

1.2. 这里的Metadata是干啥的

每个kotlin生成java的类里面都会有这样的一个Metadata的注解文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

@Metadata(
   ...
)

1.3. lambda表达式

lambda表达式是Kotlin的很重要的一个内容,简单介绍一下上面源码中涉及到的内容,抛砖引玉,有兴趣可以详细了解。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

上面的源码中用到了这么一句:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

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

表明传入了一个lambda表达式,需要无参并返回T。下面列出一些常用的简单lambda函数类型文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

//无参、无返回值的函数类型(Unit 返回类型不可省略)
() -> Unit
//接收T类型参数、无返回值的函数类型
(T) -> Unit
//接收T类型和A类型参数、无返回值的函数类型(多个参数同理)
(T,A) -> Unit
//接收T类型参数,并且返回R类型值的函数类型
(T) -> R
//接收T类型和A类型参数、并且返回R类型值的函数类型(多个参数同理)
(T,A) -> R

如何得到lambda的结果呢?上面的代码文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

val typedValue = initializer!!()

使用了()得到执行的结果,也可以使用initializer!!.invoke()得到执行结果。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

2. by - 委托

Kotlin 直接支持委托模式,更加优雅,简洁,通过关键字 by 实现委托。kotlin中的委托大致有下面几种形式:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

类委托

类的委托即一个类中定义的方法实际是调用另一个类的对象的方法来实现的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

// 定义一个接口
interface Base {
    fun print()
}

// 实现这个接口
class RealImpl(val x: Int) : Base {
    override fun print() {
        print(x)
    }
}

// 实现类委托
class Delegate(b: Base) : Base by b

上面是最简单的类委托的实现:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

  1. 定义一个接口Base
  2. 创建一个类RealImpl实现接口
  3. 创建一个类Delegate,使用关键字by,表明Deledate相关的属性方法都委托传入的b来实现

看起来迷迷糊糊的,我们看一下Delegate的java代码实现:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

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的方法来实现自己的方法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

属性委托

属性委托指的是一个类的某个属性值不是在类中直接进行定义,而是将其托付给一个代理类,从而实现对该类的属性统一管理。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

val/var <属性名>: <类型> by <表达式>文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

by 关键字之后的表达式就是委托, 属性的 get() 方法(以及set() 方法)将被委托给这个对象的 getValue() 和 setValue() 方法。属性委托不必实现任何接口, 但必须提供 getValue() 函数(对于 var属性,还需要 setValue() 函数)。该类需要包含 getValue() 方法和 setValue() 方法,且参数 thisRef 为进行委托的类的对象,prop 为进行委托的属性的对象。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

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的参数是固定的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

标准委托

1.NotNull

notNull 适用于那些无法在初始化阶段就确定属性值的场合,如果属性在赋值前就被访问的话则会抛出异常。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

用法如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

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

反编译成java代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

   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会抛出异常。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

2. Observable

observable 可以用于实现观察者模式,每次改变都会回调。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

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

3. Vetoable

vetoable与 observable一样,可以观察属性值的变化,不同的是,vetoable可以通过处理器函数来决定属性值是否生效。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

    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大的时候才会赋值成功。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

源码上ObservableVetoable类似,文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

    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的源码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

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的回调。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/47086.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/ymba/47086.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定