I have an anonymous class that needs to be initialized before the trait that it mixes in. Early initialization won't work because they don't allow one to use the 'this' reference. I can make it work if I declare the class as an inner trait with a self type, but this seems unnecessarily verbose, as the type is only used in one place in the code and is inuitive to be inlined as an anonymous class. However, I seem to find the syntax that scala will accept and meets the initialization order requirements that I have. Here is a simplified example without the extraneous detail (assume there are reasons I'm doing things this way).
trait WaitCondition[+T] {
...
}
trait EventWaitCondition[+T] extends WaitCondition[T] {
...
}
trait Event { outer =>
private[this] var _cachedWaitCondition : Option[WaitCondition[T]]
def next() : WaitCondition[T] =
//Is there a way to "inline" the defintion of NextWaitCondition
//without screwing up the initialization order?
_cachedWaitCondition.getOrElse{ new NextWaitCondition with EventWaitCondition[T] }
private[this] trait NextWaitCondition { this : WaitCondition[T] =>
outer._cache = Some(this)
....
}
....
}
So, basically, my question is that is there a way to inline the definition of NextWaitCondition as an anonymous type without changing the initialization order between NextWaitCondition and WaitCondition (i.e., so that NextWaitCondition still initializes first)?
Short answer: of course not.
We must take your word that there is just cause for the bending the laws of init order. Ponder for a moment the souls who respect the law but still suffer under it.
@xiefei's answer is not kludgy; it's sufficiently structured to effect the kludge you seek.
There is talk about deprecating DelayedInit in favor of a postConstructor hook; but you're really asking for a preConstructor hook, so why not just formalize that with a template method?
And depending on the dependency between your Foo and SubFoo, this could be a matter of preferring composition to inheritance. Then there are no games with init order.
In the following, Firstly generalizes your named solution with a template method. It has the advantage that only the use site knows or cares about it.
trait Second {
println("I must happen after first.")
}
trait SecondDelayed extends DelayedInit {
println("SecondDelayed neutral stuff")
def delayedInit(body: =>Unit) {
body // body first
println("I must be delayed after first.")
}
}
trait Firstly {
def firstly
firstly
}
object Test extends App {
def trial(t: =>Unit) {
println("----")
t
}
// candidate for least obnoxious
trial {
new Firstly with Second {
def firstly {
println("Do this firstly.")
}
println("Don't care when this happens.")
}
}
trial {
// current solution
new Something with Second
trait Something { this: Second =>
println("First code.")
}
}
trial {
// prefer anon
new Second {
println("Anon first?") // nope
}
}
trial {
// DelayedInit solution
new SecondDelayed {
println("Anon first, then other delayed.")
}
}
trial {
// the "delayed" code must be idempotent,
// or find a useful way to trigger execution;
// here, the delayed code happens twice.
class Foo extends SecondDelayed {
println("Foo wants init, too")
}
new Foo {
println("Anon first, then other delayed.")
}
}
/* early defs are only for defs, with no this
new {
println("Anon first.")
} with Second
*/
trial {
// trait code doesn't participate
new DelayedInit with Second {
def delayedInit(body: =>Unit) {
println("My crucial early init business")
body
}
}
}
}
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