是否可以确定是否初始化了lazy val而没有初始化它?
object TheApp {
lazy val optionalSubsystem = {
// ...
subsystem
}
def main(args: Array[String]) {
bootSubsystemA(this)
bootSubsystemB(this)
if (/* optionalSubsystem is initialized */) {
// more dependencies
}
}
}
答案 0 :(得分:14)
这不是你问题的真正答案,我讨厌人们这样做,但无论如何我都会去做。我认为最好的回答是:懒惰的val不适合这个,所以定义一个支持你需要的类型。
你必须将变量称为optionalSubsystem()
而不是optionalSubsystem
,但这是 A Good Thing ,因为根据你想要的设计,获得该参考是一个明显的副作用程序。
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
编辑 - 感谢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
}
这使您可以代替lazily { ... }
代替new Lazy { ... }
,而optionalSubsystem
代替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
答案 1 :(得分:3)
您可以这样做:
object TheApp {
private var _optionalSubsystemInitialized = false
def optionalSubsystemInitialized = _optionalSubsystemInitialized
lazy val optionalSubsystem = {
_optionalSubsystemInitialized = true
subsystem
}
}
在lazy val
的初始化代码中产生这样的副作用是否真的合适是另一个问题。
答案 2 :(得分:2)
但你当然可以。字段只是一个字段。
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}"
}
需要注意的是foo_?
的线程安全性依赖于延迟计算获取实例x
的监视器。有人谈论改变它。
另外,显然,仅当init值不是默认值(null.asInstanceOf[T]
)时才测试字段值。
第二种方法依赖于由惰性init加载的类Lazy$
。将Foo内的物体松散起来会更安全一些。无论如何,这种特殊的副作用是一次性的。这可能会满足子系统启动的用例。
令人惊讶的输出:
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
编译于2.11。对于2.10,请使用newTermName
代替TermName
。
答案 3 :(得分:2)
此解决方法如何?
val used = new AtomicBoolean(false)
lazy val o: String = {
used.set(true)
"aaa"
}
if (used.get()) { /* initialized */ }
答案 4 :(得分:0)
不是直接的,但为什么不像这样改变你的逻辑:
object TheApp {
lazy val optionalSubsystem = {
// ...
subsystem
// more dependencies
}
def main(args: Array[String]) {
bootSubsystemA(this)
bootSubsystemB(this)
}
}
这样“更多的依赖关系”会在最佳时间加载(包括从不需要的时候)
答案 5 :(得分:0)
为了保持不变,您必须处理状态更改。
您可以使用以下Lazy来完成您想做的事情:
trait Lazy[T] {
def act[U](f: T => U): (Lazy[T], U)
def actIfInitialized[U](f: T => U): (Lazy[T], Option[U])
def isInitialized: Boolean
}
case class UnInitializedLazy[T](builder: () => T) extends Lazy[T] {
override def isInitialized: Boolean = false
override def act[U](f: T => U): (Lazy[T], U) = {
InitializedLazy(builder()).act(f)
}
override def actIfInitialized[U](f: T => U): (Lazy[T], Option[U]) = {
(this, None)
}
}
case class InitializedLazy[T](thing: T) extends Lazy[T] {
override def isInitialized: Boolean = false
override def act[U](f: T => U): (Lazy[T], U) = {
(this, f(thing))
}
override def actIfInitialized[U](f: T => U): (Lazy[T], Option[U]) = {
(this, Some(f(thing)))
}
}
您将以这种方式使用它:
class example extends FlatSpec with Matchers {
it should "initialize and act" in {
val lazyBob: Lazy[String] = UnInitializedLazy(() => "Bob")
val (bob, upperBob) = personToUpper(lazyBob)
upperBob shouldBe "BOB"
bob.isInitialized shouldBe true
}
it should "act since it was initialized" in {
val lazyBob: Lazy[String] = UnInitializedLazy(() => "Bob")
val (bob, upperBob) = personToUpper(lazyBob)
var res: Boolean = false
upperBob shouldBe "BOB"
bob.isInitialized shouldBe true
bob.actIfInitialized(_ => res = true)
res shouldBe true
}
it should "not act since it was not initialized" in {
val lazyBob: Lazy[String] = UnInitializedLazy(() => "Bob")
var res: Boolean = false
lazyBob.isInitialized shouldBe false
lazyBob.actIfInitialized(_ => res = true)
lazyBob.isInitialized shouldBe false
res shouldBe false
}
def personToUpper(person: Lazy[String]): (Lazy[String], String) = {
// Here you will initialize it and do the computing you want.
// The interest is that you will not need to know how to instanciate Bob
// since it was defined before, you just do your computations and return a state.
person.act(p => p.toUpperCase)
}
}