如何将实现委托给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>()) { } }