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

Mar*_*rek 18 kotlin

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

class Foo(xs : ArrayList<Int>) : List<Int> by xs { }
Run Code Online (Sandbox Code Playgroud)

但这向用户展示了支持实施者.委托匿名也似乎没问题:

class Foo() : List<Int> by ArrayList<Int>() { }
Run Code Online (Sandbox Code Playgroud)

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

因此,我希望将实现委托给不在主构造函数中的属性.我想拥有的是类似的

class Foo() : List<Int> by xs {
    val xs : List<Int> = ArrayList<Int>()
}
Run Code Online (Sandbox Code Playgroud)

哪个不编译.

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

Ale*_*lov 13

目前这不可行.by-clause中的表达式仅在构造类之前计算一次,因此您无法引用该类的符号.

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

有时可行的一个有趣的解决方法是使您想要成为委托的属性,而不是使用默认值的构造函数参数.这样它在by-clause和class体中都可以访问:

class Foo(val xs: List<Int> = ArrayList<Int>()) : List<Int> by xs {
    fun bar() {
        println(xs)
    }
}
Run Code Online (Sandbox Code Playgroud)

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


Mar*_*rek 7

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

private open class FooBase(protected val xs : MutableList<Int>) : List<Int> by xs { }

class Foo() : FooBase(ArrayList()) {
    fun bar() {
        xs.add(5)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

注意:虽然它有效,但我从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>()) { }
}
Run Code Online (Sandbox Code Playgroud)