编写Kotlin util函数,在初始化程序中提供自引用

hot*_*key 10 initialization this lazy-evaluation kotlin

我试图将我的黑客从另一个问题的答案中推广出来.

它应该提供一种方法来引用一个尚未在其初始化器中构造的值(当然,不是直接,而是在lambdas和对象表达式中).

我现在所拥有的:

class SelfReference<T>(val initializer: SelfReference<T>.() -> T) {
    val self: T by lazy {
        inner ?: throw IllegalStateException("Do not use `self` until initialized.")
    }

    private val inner = initializer()
}

fun <T> selfReference(initializer: SelfReference<T>.() -> T): T {
    return SelfReference(initializer).self
}
Run Code Online (Sandbox Code Playgroud)

它有效,请看这个例子:

class Holder(var x: Int = 0,
             val action: () -> Unit)

val h: Holder = selfReference { Holder(0) { self.x++ } }
h.action()
h.action()
println(h.x) //2
Run Code Online (Sandbox Code Playgroud)

但在这一点上,initializer引用构造值的方式是self属性.

我的问题是:有没有办法重写SelfReference所以initializer传递参数(或接收器)而不是使用self属性?这个问题可以重新表述为:有没有办法将一个延迟评估的接收器/参数传递给函数或以某种方式实现这种语义?

改进代码的其他方法有哪些?


UPD:一种可能的方法是传递一个返回的函数self,因此它将被用作it()内部函数initializer.还在寻找其他的.

Aro*_*Aro 0

有没有办法重写 SelfReference 以便初始化程序传递参数(或接收器)而不是使用 self 属性?这个问题可以重新表述为:有没有办法将延迟评估的接收者/参数传递给函数或以某种方式实现此语义?

我不确定我完全理解您的用例,但这可能就是您正在寻找的:

fun initHolder(x: Int = 0, holderAction: Holder.() -> Unit) : Holder {
    var h: Holder? = null
    h = Holder(x) { h!!.holderAction() }
    return h
}

val h: Holder = initHolder(0) { x++ }
h.action()
h.action()
println(h.x) // 2
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为holderActionlambda 带有接收者 ( Holder.() -> Unit),允许 lambda 访问接收者的成员。

这是一个通用解决方案,因为您可能无法更改相应Holder构造函数的签名。值得注意的是,此解决方案不需要打开类,否则可以使用辅助构造函数对子类完成类似的方法。

SelfReference当只有少数类需要更改时,我更喜欢此解决方案而不是创建类。

您可能需要检查null而不是使用,!!以便抛出有用的错误。如果 Holder 调用action它的构造函数或 init 块,您将得到一个空指针异常。