I'm trying to generalize my hack from an answer to another question.
It should provide a way to reference a value which is not constructed yet inside its initializer (of course, not directly, but in lambdas and object expressions).
What I have at the moment:
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
}
It works, see this example:
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
But at this point the way in which initializer
references the constructed value is self
property.
And my question is: is there a way to rewrite SelfReference
so that initializer
is passed an argument (or a receiver) instead of using self
property? This question can be reformulated to: is there a way to pass a lazily evaluated receiver/argument to a function or achieve this semantics some way?
What are the other ways to improve the code?
self
, thus it would be used as it()
inside the initializer
. Still looking for other ones.
The best I have managed to produce while still being completely generic is this:
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()
operator fun invoke(): T = self
}
Adding the invoke operator lets you use it in the following way:
val h: Holder = selfReference { Holder(0) { this().x++ } }
This is the closest I got to make it look like something you would "normally" write.
Sadly I think it is not possible to get completely rid of a explicit access to the element. Since to do that you would need a lambda parameter of type T.() -> T
but then you wouldn't be able to call that parameter without an instance of T
and being T a generic there is no clean and safe way to acquire this instance.
But maybe I'm wrong and this helps you think of a solution to the problem
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With