两分钟了解kotlin --代理(委托)

229 阅读2分钟

图像 171.png

1. 对象代理。

以下是一个代理使用类

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(b: Base) : Base by b

fun main() {
    val b = BaseImpl(10)
    Derived(b).print()
}

我们将kt代码转换成java代码。去掉了相关注解信息。

public interface Base {
   void print();
}

public final class BaseImpl implements Base {
   private final int x;

   public void print() {
      int var1 = this.x;
      boolean var2 = false;
      System.out.print(var1);
   }

   public final int getX() {
      return this.x;
   }

   public BaseImpl(int x) {
      this.x = x;
   }
}

public final class Derived implements Base {
   private final Base $$delegate_0;
   public Derived(@NotNull Base b) {
      Intrinsics.checkNotNullParameter(b, "b");
      super();
      this.$$delegate_0 = b;
   }
   public void print() {
      this.$$delegate_0.print();
   }
}

public final class BaseKt {
   public static void main(String[] var0) {
      main();
   }
   public static final void main() {
      BaseImpl b = new BaseImpl(10);
      (new Derived((Base)b)).print();
   }

}


可以看到对应的java代码如下几点

  1. 首先Derived保存了Base接口的直接实现$$delegate_0。
  2. 编译器为Derived实现Base接口,重写接口方法,把接口方法转交给$$delegate_0对象。 这样Derived就拥有了接口的能力,这一过程和我们编写的静态代理方法式一样的。 kotlin使用by关键字,隐藏了这已实现过程。

委托极大增强了类的表现力,只要知道接口,以及接口的实现,使用by关键字就能够增强一个类的功能。

2.属性代理

import kotlin.reflect.KProperty
class Example {
    var p: String by Delegate()
}
class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }

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

其中thisRef是本类对象,property可以获取到代理属性的信息。

下面是转成java的代码。

public final class Delegate {
   @NotNull
   public final String getValue(@Nullable Object thisRef, @NotNull KProperty property) {
      Intrinsics.checkNotNullParameter(property, "property");
      return thisRef + ", thank you for delegating '" + property.getName() + "' to me!";
   }
   public final void setValue(@Nullable Object thisRef, @NotNull KProperty property, @NotNull String value) {
      Intrinsics.checkNotNullParameter(property, "property");
      Intrinsics.checkNotNullParameter(value, "value");
      String var4 = value + " has been assigned to '" + property.getName() + "' in " + thisRef + '.';
      boolean var5 = false;
      System.out.println(var4);
   }
}

public final class Example {
   static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Example.class, "p", "getP()Ljava/lang/String;", 0))};
   @NotNull
   private final Delegate p$delegate = new Delegate();

   @NotNull
   public final String getP() {
      return this.p$delegate.getValue(this, $$delegatedProperties[0]);
   }

   public final void setP(@NotNull String var1) {
      Intrinsics.checkNotNullParameter(var1, "<set-?>");
      this.p$delegate.setValue(this, $$delegatedProperties[0], var1);
   }
}

public final class ExampleKt {
   public static final void main() {
      String var0 = (new Example()).getP();
      boolean var1 = false;
      System.out.print(var0);
   }
   public static void main(String[] var0) {
      main();
   }
}

可以看到编译后的java做了一下几件事

  1. 生成一个属性代理对象p$delegate(Delegate)并保存备用。
  2. 为属性P增加了getP() 和setP()方法,同时getP() 和setP()方法转发给pdelegatesetValue()\getValue()方法。另外属性p被放到一个KPropery[]数组中了,这个可以代理转发的时候传给pdelegate的setValue()\getValue()方法。 另外 属性p被放到一个KPropery[] 数组中了,这个可以代理转发的时候传给pdelegate,用力来获取p的信息。

属性代理丰富了类属性的表现了,可以通过代理的方式增强和修改一个类属性。

整体的代理思想:

持有被代理的对象,在调用属性或者方法的时候把数据转发给代理对象。委托就是在语法层面上做了一层转换。

类代理(委托)在编译时做了如下的事情

  • 接口(Base)、接口的实现(BaseImpl)不变
  • 被代理的类(Derived)实现代理接口(Base),重写接口方法,在接口方法中调用接口实现者(BaseImpl)对应的方法。接口实现者(BaseImpl)就接管了被代理类(Derived)中的代理接口(Base)方法。

本质上还是类似于静态代理。

属性代理(属性委托)在编译时做了如下的事情

  • 代理对象(Delegate)必须要set/get方法,编译期间不变。
  • 给使用属性代理的类(Example)中增加一个代理对象(Delegate)。给被委托P的属性生成getP() 和setP()方法。把实际的getXX() 和setXX()方法转发到代理对象(Delegate)的setValue()\getValue()中去。让代理接管了XX 属性的获取和重置。

代理极大增强了类和属性的表现力,实现了轻量级的扩展和修改,相对于继承更加灵活,更易于使用。