`lazy val` 在异常情况下表现得像 `def` 是正确的行为吗?
Is it correct behaviour that `lazy val` acts like `def` in case of exception?
我注意到 lazy val
重复计算了几次(以防异常):
scala> lazy val aaa = {println("calc"); sys.error("aaaa")}
aaa: Nothing = <lazy>
scala> aaa
calc
java.lang.RuntimeException: aaaa
at scala.sys.package$.error(package.scala:27)
at .aaa$lzycompute(<console>:7)
at .aaa(<console>:7)
... 33 elided
scala> aaa
calc
java.lang.RuntimeException: aaaa
at scala.sys.package$.error(package.scala:27)
at .aaa$lzycompute(<console>:7)
at .aaa(<console>:7)
... 33 elided
不应该是这样的吗:
scala> aaa
calc
java.lang.RuntimeException: Not Initialized!
caused by
java.lang.RuntimeException: aaaa
scala> aaa
java.lang.RuntimeException: Not Initialized!
caused by
java.lang.RuntimeException: aaaa
在 this post 中,他们很好地解释了 lazy val
是如何被 Scala 编译器编译的。基本上,如果表达式的评估失败,则不会设置指示 lazy val
包含其数据的指示符位。
更新1:
我认为采用第一种方法的一个原因可能是第二种方法可以通过使用两个 lazy val
来模拟,而不会给底层实现增加多个可变变量的负担:
scala> lazy val _aaa = Try {println("calc"); sys.error("aaaa")}
_aaa: scala.util.Try[Nothing] = <lazy>
scala> lazy val aaa = _aaa.get
aaa: Nothing = <lazy>
scala> aaa
calc
java.lang.RuntimeException: aaaa
at scala.sys.package$.error(package.scala:27)
at $anonfun$_aaa.apply(<console>:10)
at $anonfun$_aaa.apply(<console>:10)
at scala.util.Try$.apply(Try.scala:191)
at ._aaa$lzycompute(<console>:10)
at ._aaa(<console>:10)
at .aaa$lzycompute(<console>:11)
at .aaa(<console>:11)
... 33 elided
scala> aaa
java.lang.RuntimeException: aaaa
at scala.sys.package$.error(package.scala:27)
at $anonfun$_aaa.apply(<console>:10)
at $anonfun$_aaa.apply(<console>:10)
at scala.util.Try$.apply(Try.scala:191)
at ._aaa$lzycompute(<console>:10)
at ._aaa(<console>:10)
at .aaa$lzycompute(<console>:11)
at .aaa(<console>:11)
... 33 elided
更新2:
正如 @Silly Freak 在他的评论中提出的那样,
scala> lazy val _aaa = Try {println("calc"); sys.error("aaaa")}
_aaa: scala.util.Try[Nothing] = <lazy>
scala> def aaa = _aaa.get
aaa: Nothing
可能会更好,因为我们可以避免有两个 lazy val
。
我注意到 lazy val
重复计算了几次(以防异常):
scala> lazy val aaa = {println("calc"); sys.error("aaaa")}
aaa: Nothing = <lazy>
scala> aaa
calc
java.lang.RuntimeException: aaaa
at scala.sys.package$.error(package.scala:27)
at .aaa$lzycompute(<console>:7)
at .aaa(<console>:7)
... 33 elided
scala> aaa
calc
java.lang.RuntimeException: aaaa
at scala.sys.package$.error(package.scala:27)
at .aaa$lzycompute(<console>:7)
at .aaa(<console>:7)
... 33 elided
不应该是这样的吗:
scala> aaa
calc
java.lang.RuntimeException: Not Initialized!
caused by
java.lang.RuntimeException: aaaa
scala> aaa
java.lang.RuntimeException: Not Initialized!
caused by
java.lang.RuntimeException: aaaa
在 this post 中,他们很好地解释了 lazy val
是如何被 Scala 编译器编译的。基本上,如果表达式的评估失败,则不会设置指示 lazy val
包含其数据的指示符位。
更新1:
我认为采用第一种方法的一个原因可能是第二种方法可以通过使用两个 lazy val
来模拟,而不会给底层实现增加多个可变变量的负担:
scala> lazy val _aaa = Try {println("calc"); sys.error("aaaa")}
_aaa: scala.util.Try[Nothing] = <lazy>
scala> lazy val aaa = _aaa.get
aaa: Nothing = <lazy>
scala> aaa
calc
java.lang.RuntimeException: aaaa
at scala.sys.package$.error(package.scala:27)
at $anonfun$_aaa.apply(<console>:10)
at $anonfun$_aaa.apply(<console>:10)
at scala.util.Try$.apply(Try.scala:191)
at ._aaa$lzycompute(<console>:10)
at ._aaa(<console>:10)
at .aaa$lzycompute(<console>:11)
at .aaa(<console>:11)
... 33 elided
scala> aaa
java.lang.RuntimeException: aaaa
at scala.sys.package$.error(package.scala:27)
at $anonfun$_aaa.apply(<console>:10)
at $anonfun$_aaa.apply(<console>:10)
at scala.util.Try$.apply(Try.scala:191)
at ._aaa$lzycompute(<console>:10)
at ._aaa(<console>:10)
at .aaa$lzycompute(<console>:11)
at .aaa(<console>:11)
... 33 elided
更新2:
正如 @Silly Freak 在他的评论中提出的那样,
scala> lazy val _aaa = Try {println("calc"); sys.error("aaaa")}
_aaa: scala.util.Try[Nothing] = <lazy>
scala> def aaa = _aaa.get
aaa: Nothing
可能会更好,因为我们可以避免有两个 lazy val
。