Is it possible to determine if a lazy val is initialized, without initializing it?
object TheApp {
lazy val optionalSubsystem = {
// ...
subsystem
}
def main(args: Array[String]) {
bootSubsystemA(this)
bootSubsystemB(this)
if (/* optionalSubsystem is initialized */) {
// more dependencies
}
}
}
There is a way, but you have to access the delegate object which is returned by lazy {} : val messageBrokerDelegate = lazy { MessageBroker() } val messageBroker by messageBrokerDelegate if(messageBrokerDelegate. isInitialized()) ... isInitialized is a public method on interface Lazy<T> , here are the docs.
Lazy initialization means that whenever an object creation seems expensive, the lazy keyword can be stick before val. This gives it the advantage to get initialized in the first use i.e. the expression inbound is not evaluated immediately but once on the first access. Example: // Scala program of Lazy val.
lazy val is a language feature where the initialization of a val is delayed until it is accessed for the first time.
Scala provides a nice language feature called lazy val that defers the initialization of a variable. The lazy initialization pattern is common in Java programs. Though it seems tempting, the concrete implementation of lazy val has some subtle issues.
This isn't really an answer to your question, and I hate when people do this, but I'm going to do it anyway. I think the best response is: A lazy val isn't appropriate for this, so define a type that does support what you need.
You'll have to refer to the variable as optionalSubsystem()
rather than optionalSubsystem
, but that's A Good Thing, because with the design you want, obtaining that reference is an observably side-effecting procedure.
class Lazy[A](f: => A, private var option: Option[A] = None) {
def apply(): A = option match {
case Some(a) => a
case None => val a = f; option = Some(a); a
}
def toOption: Option[A] = option
}
scala> val optionalSubsystem = new Lazy { "a" }
optionalSubsystem: Lazy[java.lang.String] = Lazy@1210267
scala> optionalSubsystem.toOption.isDefined
res1: Boolean = false
scala> optionalSubsystem()
res2: java.lang.String = a
scala> optionalSubsystem.toOption.isDefined
res12: Boolean = true
Edit - Here's another revision with some modifications thanks to Tomas Mikula:
import scala.language.implicitConversions
object Lazy {
def lazily[A](f: => A): Lazy[A] = new Lazy(f)
implicit def evalLazy[A](l: Lazy[A]): A = l()
}
class Lazy[A] private(f: => A) {
private var option: Option[A] = None
def apply(): A = option match {
case Some(a) => a
case None => val a = f; option = Some(a); a
}
def isEvaluated: Boolean = option.isDefined
}
This lets you write lazily { ... }
instead of new Lazy { ... }
, and optionalSubsystem
instead of optionalSubsystem()
.
scala> import Lazy._
import Lazy._
scala> val optionalSubsystem = lazily { "a" }
optionalSubsystem: Lazy[String] = Lazy@3d0d54
scala> optionalSubsystem.isEvaluated
res0: Boolean = false
scala> optionalSubsystem: String
res1: String = a
scala> optionalSubsystem.isEvaluated
res2: Boolean = true
You can do something like this:
object TheApp {
private var _optionalSubsystemInitialized = false
def optionalSubsystemInitialized = _optionalSubsystemInitialized
lazy val optionalSubsystem = {
_optionalSubsystemInitialized = true
subsystem
}
}
Whether is it really appropriate to have such side effects in the initialization code of a lazy val
is another question.
But of course you can. A field is just a field.
package lazyside
object Lazy
class Foo {
lazy val foo = 7
lazy val bar = { Lazy ; 8 }
}
object Test extends App {
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
val x = new Foo
// method 1: reflect the underlying field
val im = cm reflect x
val f = (typeOf[Foo] declaration TermName("foo")).asTerm.accessed.asTerm
def foo_? = x synchronized ((im reflectField f).get != 0)
def yn(b: Boolean) = if (b) "yes" else "no"
Console println s"Is foo set yet? ${yn(foo_?)}"
// method 2: check a benign side effect like a class load
val m = classOf[ClassLoader].getDeclaredMethod("findLoadedClass", classOf[String])
m setAccessible true
def bar_? = (m invoke (x.getClass.getClassLoader, "lazyside.Lazy$")) != null
Console println s"Is bar set yet? ${yn(bar_?)}"
Console println s"I see that foo is ${x.foo}."
Console println s"Is foo set yet? ${yn(foo_?)}"
Console println s"I see that bar is ${x.bar}."
Console println s"Is bar set yet? ${yn(bar_?)}"
Console println s"I see that x is loaded by a ${x.getClass.getClassLoader.getClass}"
}
The caveat is that thread-safety of foo_?
relies on the lazy computation acquiring the monitor of the instance x
. There is talk of changing that.
Also, obviously, testing the field value only works if the init value is not the default value (null.asInstanceOf[T]
).
The second method relies on the class Lazy$
getting loaded by the lazy init. It would be a bit safer to squirrel the object inside Foo. In any case, that particular side-effect is one-shot. That might satisfy the use case of subsystem start-up.
With the unsurprising output:
Is foo set yet? no Is bar set yet? no I see that foo is 7. Is foo set yet? yes I see that bar is 8. Is bar set yet? yes I see that x is loaded by a class scala.reflect.internal.util.ScalaClassLoader$URLClassLoader
Compiled in 2.11. For 2.10, use newTermName
instead of TermName
.
How about this workaround?
val used = new AtomicBoolean(false)
lazy val o: String = {
used.set(true)
"aaa"
}
if (used.get()) { /* initialized */ }
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