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?
UPD: One possible way is to pass a function that returns
self
, thus it would be used as it()
inside the initializer
. Still looking for other ones.
I'm not sure I completely understand your use case but this may be what you're looking for:
This works because
holderAction
is a lambda with a receiver (Holder.() -> Unit
) giving the lambda access to the receiver's members.This is a general solution since you may not be able to change the signature of the respective
Holder
constructor. It may be worth noting this solution does not require the class to be open, otherwise a similar approach could be done with a subclass using a secondary constructor.I prefer this solution to creating a
SelfReference
class when there are only a few number of classes that need the change.You may want to check for
null
instead of using!!
in order to throw a helpful error. If Holder callsaction
in it's constructor or init block, you'll get a null pointer exception.The best I have managed to produce while still being completely generic is this:
Adding the invoke operator lets you use it in the following way:
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 ofT
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
I'm pretty sure you can achieve the same results in a more readable and clear way using something like this:
and later use
Using as example your quoted question https://stackoverflow.com/a/35050722/2196460 you can do this:
Inside the selfReferenced block you can use the outer object with no restrictions. The only thing you should take care of, is declaring the type explicitly to avoid recursive type checking issues.