Kotlin:将复合 class 属性公开为 public 主机 class 属性

Kotlin: Expose composite class properties as public host class properties

假设我们有一个 Composite class

class Composite(val one: Int, val two: Int)

和一个Hostclass

class Host(val comp: Composite)

现在我们可以打印 Composite 对象的属性

fun hostTest() {
    val comp = Composite(2, 3)
    val hst = Host(comp)

    println(hst.comp.one)
    println(hst.comp.one)
}

在 Kotlin 中是否可以将 Composite 属性公开为 Host class 的直接属性? 所以,我想写这样的东西:

fun hostTest() {
    val comp = Composite(2, 3)
    val hst = Host(comp)

    println(hst.one)
    println(hst.one)
}

当然,可以在 Host 中创建代理属性,但我希望 Kotlin 作为一种实用语言能够直接支持这一点。

这不是直接可能的,但是你可以通过implementation by delegation来模拟它,这需要你将Composite的属性移动到一个接口:

interface Composite {
    val one: Int
    val two: Int
}

class CompositeImpl(
    override val one: Int, 
    override val two: Int
) : Composite

class Host(val comp: Composite) : Composite by comp

(runnable sample)

如果需要,您可以制作 comp 属性 private,或者您可以使用 [=15= 中提供的任何其他 Composite 实例]在Composite by ...子句中的初始化时间,甚至就地构造一些Composite

Kotlin 对此的解决方案是 delegation

您可以通过多种方式实现,具体选择哪一种取决于您的需要。首先,您的 Composite:

需要一个 interface
interface Composite {
    val one: Int
    val two: Int
}

和默认实现:

class DefaultComposite(override val one: Int,
                       override val two: Int) : Composite

然后您可以使用 by 关键字从 Host:

委托给 Composite 的实例
class Host(val composite: Composite) : Composite by composite

如果您对 composite 有合理的默认值:

class CompositeWithDefaults(override val one: Int = 1,
                            override val two: Int = 2) : Composite

那么你甚至不必将 Composite 作为构造函数参数传递:

class Host() : Composite by CompositeWithDefaults()

或者您可以将其字段传递给 Host:

class Host(one: Int, two: Int) : Composite by DefaultComposite(one, two)

或者为其设置默认值:

class Host(composite: Composite = CompositeWithDefaults()) : Composite by composite

注意:你不应该委托给可变的 属性,因为使用 by 时生成的字节码将为你的委托使用一个内部字段并且替换原来的对象不会有任何效果。我已经写过这个 here.

如果 compositevar:

,那么生成的 Java 代码就是这样的
public final class Host implements Composite {
   @NotNull
   private Composite composite;
   // $FF: synthetic field
   private final Composite $$delegate_0;

   @NotNull
   public final Composite getComposite() {
      return this.composite;
   }

   public final void setComposite(@NotNull Composite var1) {
      Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
      this.composite = var1;
   }

   public Host(@NotNull Composite composite) {
      Intrinsics.checkParameterIsNotNull(composite, "composite");
      super();
      this.$$delegate_0 = composite;
      this.composite = composite;
   }

   public int getOne() {
      return this.$$delegate_0.getOne();
   }

   public int getTwo() {
      return this.$$delegate_0.getTwo();
   }
}

请注意 setter 不会设置 $$delegate_0,而是 composite