kotlin系列知识卡片 - by关键字

123 阅读2分钟

前言
随着kotlin在Android日常开发中越来越频繁地使用,笔者觉得有必要对kotlin的一些特性做一些原理的梳理,所谓知其然知其所以然,才能更好让kotlin服务于我们的业务开发,所以会做一个kotlin系列,每个知识点简短,像个小卡片一样,便于理解和快速回顾。所有的源码为:Kotlin 标准库版本为1.9.10。

by关键字

by关键字是kotlin提供给开发者一个高效的语法糖,用于实现委托模式,委托本身是一种设计模式,允许将某些功能或属性的实现委托给另一个类或者对象。目的是提升代码的复用性和维护性。帮助我们提高编码效率。下文将by关键字的使用介绍及反编译看委托的实现细节。

使用


kotlin中的委托分为:属性委托、类委托。

  • 委托属性 属性委托是将一个对象的getter和setter委托给另一个类的实例。通过修改getter/setter操作符的逻辑,来代理被委托的属性的读写行为,例如:
// 实现了getter和setter操作符函数,当其他属性被委托到这个类时,getter和setter将被其代理执行
class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String { ... }
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { ... }
}

class Demo {
    // 对test对象的getter和setter将被Delegate对象代理
    private var test by Delegate()
}

  • 委托接口 委托者和被委托者通过实现同一个接口来完成类行为的委托。例如:
// 定义一个通用接口
interface IDelegate {
    fun getAAA() {}
    fun getBBB() {}
}

// 使用委托
class Demo(delegate: IDelegate): IDelegate by delegate 

原理

  • 委托属性 我们通过反编译kotlin代码来看使用部分的demo的委托属性的实现

反编译后我们可以看到编译器帮我们生成了一个delegate实例,并抹掉了test参数,生成了与其名称对应的setter和getter函数,函数的实现交由delegate实现。

@Metadata(...)
public static final class Demo {
   // $FF: synthetic field
   static final KProperty[] $$delegatedProperties = new KProperty[]{Reflection.mutableProperty1(new MutablePropertyReference1Impl(Demo.class, "test", "getTest()Ljava/lang/String;", 0))};
   private final Delegate test$delegate = new Delegate();

   private final String getTest() {
      return this.test$delegate.getValue(this, $$delegatedProperties[0]);
   }

   private final void setTest(String var1) {
      this.test$delegate.setValue(this, $$delegatedProperties[0], var1);
   }
}
  • 委托接口 我们通过反编译kotlin代码来看使用部分的demo的委托接口的实现

从反编译出来的代码中我们可以看到kotlin编译器帮我们生成了接口类中所有的接口,并转交给构造时传入的delegate参数来实现对应接口的能力


@Metadata(...)
public final class Demo implements IDelegate {
   // $FF: synthetic field
   private final IDelegate $$delegate_0;

   public Demo(@NotNull IDelegate delegate) {
      Intrinsics.checkNotNullParameter(delegate, "delegate");
      super();
      this.$$delegate_0 = delegate;
   }

   public void getAAA() {
      this.$$delegate_0.getAAA();
   }

   public void getBBB() {
      this.$$delegate_0.getBBB();
   }
}