Kotlin中内置的委托

委托可以帮助您将任务委托给其他对象,并提供更好的代码重用,您可以在如下文章中了解更多相关内容。

Kotlin中的属性委托,用起来!

Kotlin中的接口委托是什么?

Kotlin不仅支持一种通过关键字实现委托的简单方法,而且还在Kotlin标准库中提供了内置委托,如lazy()、observable()、vetoable()和notull()。让我们看看这些内置委托以及它们在底层是如何工作的。

1、lazy()

lazy():惰性求值委托

lazy()函数是一个属性委托,它帮助您惰性地初始化属性,也就是当它们第一次被访问时。Lazy()对于创建成本较高的对象非常有用。

lazy()接受两个参数,LazyThreadSafetyMode枚举值和lambda。

LazyThreadSafetyMode参数指定如何在不同线程之间同步初始化,lazy()默认值使用LazyThreadSafetyMode.SYNCHRONIZED,这意味着初始化是线程安全的,代价是显式同步对性能的轻微影响。

public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)


public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
    when (mode) {
        LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
        LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
        LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
    }

在第一次访问属性时执行lambda,然后存储它的值以供将来访问。

class Product(price: Double, oldPrice: Double) {
    val plummetPrice :Double by lazy {
        oldPrice - price
    }
}
底层实现

在检查反编译的Java代码时,我们看到Kotlin编译器为Lazy委托创建了一个Lazy类型的引用。

@NotNull
private final Lazy plummetPrice$delegate;

这个委托是由LazyKt.lazy()函数使用您指定的lambda和线程安全模式参数初始化的。

this.plummetPrice$delegate = LazyKt.lazy((Function0)(new Function0() {
         // $FF: synthetic method
         // $FF: bridge method
         public Object invoke() {
            return this.invoke();
         }

         public final double invoke() {
            return oldPrice - price;
         }
      }));

让我们看一看lazy()的源代码。因为lazy()函数使用的默认值是LazyThreadSafetyMode.SYNCHRONIZED返回一个SynchronizedLazyImpl类类型的Lazy对象。

public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

当委托的属性第一次被访问时,SynchronizedLazyImpl的getValue()函数将被调用,它将初始化同步块中的属性。

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

这保证了惰性对象以线程安全的方式初始化,但增加了同步块的性能成本。

注意:如果你确定这个资源将由一个线程初始化,你可以传递LazyThreadSafetyMode.NONElazy(),函数在惰性初始化时不会使用synchronized。但是,请记住LazyThreadSafetyMode.NONE不会改变惰性初始化的同步性质。由于惰性初始化是同步的,因此在第一次访问时,它所花费的时间与非惰性初始化对象所花费的时间相同。如果访问它,这意味着需要很长时间初始化的对象仍然可以阻塞UI线程。

val lazyValue: String by lazy(LazyThreadSafetyMode.NONE) {" lazy "}

2、observable

observable:可观察属性委托 用于在属性值变化时自动调用指定的回调函数

delegate .observable()是Kotlin标准库的另一个内置委托。观察者是一种设计模式,在这种模式中,对象维护其依赖项列表(称为观察者),并在其状态更改时自动通知它们。当一个值发生变化时,需要通知多个对象,而不是让每个依赖对象定期调用并检查资源是否更新时,这种模式非常有用。

Observable()接受两个参数:初始值和一个侦听器,当值被修改时将被调用.observable()创建一个ObservableProperty对象,在每次调用setter时执行你传递给委托的lambda。

class Product{
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
    name = "苹果" // 输出 "<no name> -> 苹果"
    name = "香蕉" // 输出 "苹果 -> 香蕉"

}

查看反编译的Product类,我们看到Kotlin编译器生成了一个扩展ObservableProperty的类。该类还实现了一个名为afterChange()的函数,该函数具有传递给可观察委托的lambda函数。

protected void afterChange(@NotNull KProperty property, Object oldValue, Object newValue) {
      Intrinsics.checkNotNullParameter(property, "property");
      String var4 = (String)newValue;
      String old = (String)oldValue;
      int var7 = false;
      String var8 = old + " -> " + var4;
      System.out.println(var8);
   }

afterChange()函数由父类ObservableProperty的setter调用。这意味着每当调用方为name设置一个新值时,setter将自动调用afterChange()函数,这样所有侦听器都收到关于更改的通知。

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

您还可以在反编译代码中看到对beforeChange()的调用。beforeChange()不是被可观察委托使用的,而是下一个将要出现的委托vetoable()做参考。

3、vetoable

vetoable:可否决属性委托 用于在属性值变化时检查新值是否合法,如果不合法则回滚到旧值。

Vetoable()是另一个内置委托,其中属性将否决权委托给其值。与observable()委托类似,vetoable()接受两个参数:初始值和监听器,当任何调用者想要修改属性的值时将调用该监听器。

var age: Int by Delegates.vetoable(0) {
    prop, old, new ->
    if (new >= 0) {
        true // 新值合法,允许更新
    } else {
        false // 新值不合法,回滚到旧值
    }
}
age = 20
println(age) // 输出 "20"
age = -10
println(age) // 输出 "20",因为新值不合法被否决了

如果lambda条件返回true,属性值将被修改,否则值将保持不变。在如上情况下,如果调用方试图更新小于0,则将保留当前值。

看一下反编译的类,Kotlin生成了一个扩展ObservableProperty的新类。生成的类包括我们在beforeChange()函数中传递的lambda,它将在值设置之前由setter调用。

public final class Person$$special$$inlined$vetoable$1 extends ObservableProperty {
   public Person$$special$$inlined$vetoable$1(Object $initialValue) {
      super($initialValue);
   }

   protected boolean beforeChange(@NotNull KProperty property, Object oldValue, Object newValue) {
      Intrinsics.checkNotNullParameter(property, "property");
      int var4 = ((Number)newValue).intValue();
      int old = ((Number)oldValue).intValue();
      int var7 = false;
      return var4 >= 0;
   }
}

4、notNull

notNull:非空属性委托 用于确保属性在第一次访问前已经被初始化。如果在访问前未被初始化,则会抛出异常。

Kotlin标准库提供的最后一个内置委托是delegats.notull()。 notNull()只是允许属性在稍后进行初始化。notNull()类似于lateinit。在大多数情况下,lateinit是首选,因为notNull()会为每个属性创建一个额外的对象。但是,可以对原始类型使用notNull(), lateinit不支持这种类型。

val fullname: String by Delegates.notNull<String>()

notNull()使用一种特殊类型的ReadWriteProperty,名为nottnullvar。

查看反编译的代码,fullname属性是用nottnull()函数初始化的。

this.fullname$delegate = Delegates.INSTANCE.notNull()

该函数返回一个nottnullvar对象。

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

nottnullvar类只是持有一个泛型的可空的内部引用,如果任何代码在值初始化之前调用getter,则抛出IllegalStateException()。

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

总结

Kotlin 内置了一些属性委托(Delegate)类,它们是:

  • lazy:惰性求值委托,用于将属性的初始化推迟到第一次访问时。

  • observable:可观察属性委托,用于在属性值变化时自动调用指定的回调函数

  • vetoable:可否决属性委托,用于在属性值变化时检查新值是否合法,如果不合法则回滚到旧值。

  • notNull:非空属性委托,用于确保属性在第一次访问前已经被初始化。如果在访问前未被初始化,则会抛出异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值