如何将实现委托给Kotlin中的一个属性?

Kotlin使我能够通过委托一个主构造器参数来实现一个接口,如下所示:

class Foo(xs : ArrayList<Int>) : List<Int> by xs { } 

但是,这展示了支持实施者给用户。 委托给匿名人员似乎也没关系:

 class Foo() : List<Int> by ArrayList<Int>() { } 

这隐藏了实现细节,但我们无法访问接口提供的功能,在这种情况下,这是可变性的。

因此,我想将实现委托给一个不在主要构造函数中的属性。 我想要的是类似于

 class Foo() : List<Int> by xs { val xs : List<Int> = ArrayList<Int>() } 

不编译。

是否有可能在类体中明确定义一个属性,并且仍然能够将实现委托给它?

目前这是不可能的。 在by clause中的表达式只在构造类之前计算一次,所以不能引用该类的符号。

在问题追踪器中有一个请求来允许这个,尽管在Kotlin 1.0中几乎肯定不会支持它。

一个有趣的解决方法, 有时候工作是使你想成为一个委托的属性,而不是默认值的构造函数参数。 这样就可以在子句和类体中访问:

 class Foo(val xs: List<Int> = ArrayList<Int>()) : List<Int> by xs { fun bar() { println(xs) } } 

请记住, xs in xs在这里仍然只计算一次,所以即使xs是一个var属性,也只会使用构造函数中提供的默认值。 这不是一个通用的解决方案,但有时它可以提供帮助。

扩展Alexander Udalov的答案,我想出了一个使用私有基类的解决方案

 private open class FooBase(protected val xs : MutableList<Int>) : List<Int> by xs { } class Foo() : FooBase(ArrayList()) { fun bar() { xs.add(5) } } 

现在,我可以访问支持接口实现的属性,但不限于该接口提供的操作,同时仍然隐藏用户的实际实现。

注意:尽管它起作用,但是从EXPOSED_SUPER_CLASS检查中得到以下IntelliJ IDEA 15 CE的警告: 不推荐:子类有效可见性“public”应该与其超类有效可见性“private”相同或更小 。 我不太确定这里弃用的部分是什么意思 – 这个警告是否会在将来被删除,或者在某些时候不会被编译。 无论如何,我们实际上并不需要使用private open类, abstract或者简单地open就可以,因为即使用户被允许创建一个FooBase的实例,也没有太多可以做的事情。

更新:

实际上有一个简单而紧凑的解决方案,不会使用任何可疑的行为:

 class Foo private constructor(private val xs: ArrayList<Int>) : List<Int> by xs { constructor() : this(ArrayList<Int>()) { } }