Kotlin:将复合类属性公开为公共宿主类属性

时间:2018-10-11 12:07:04

标签: kotlin properties composition class-design

假设我们有一个Composite

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

和一个Host

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类的直接属性? 所以,我想写这样的东西:

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

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

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

2 个答案:

答案 0 :(得分:1)

在Kotlin中解决此问题的方法是delegation

您可以通过多种方式进行操作,这取决于您的需求,您应该选择哪种方式。首先,您的interface需要一个Composite

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

和默认实现:

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

然后,您可以使用by关键字从Composite委托给Host的实例:

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

请注意,设置器没有设置$$delegate_0,而是设置了composite

答案 1 :(得分:0)

这不可能直接实现,但是您可以通过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,也可以使用Composite初始化时可用的Host的任何其他实例。 Composite by ...子句,甚至就地构建一些Composite