`lazy val` 在异常情况下表现得像 `def` 是正确的行为吗?
Posted
技术标签:
【中文标题】`lazy val` 在异常情况下表现得像 `def` 是正确的行为吗?【英文标题】:Is it correct behaviour that `lazy val` acts like `def` in case of exception? 【发布时间】:2015-10-22 22:36:37 【问题描述】:我注意到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
【问题讨论】:
前段时间我是这么读的。 【参考方案1】:在this 帖子中,他们很好地解释了lazy val
是如何由Scala 编译器编译的。基本上,如果表达式的求值失败,则不会设置 lazy val
包含其数据的指示位。
更新1:
我认为采用第一种方法的一个原因可能是第二种方法可以通过使用两个 lazy val
s 来模拟,而无需使用多个 volatile 变量给底层实现带来负担:
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$1.apply(<console>:10)
at $anonfun$_aaa$1.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$1.apply(<console>:10)
at $anonfun$_aaa$1.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
s。
【讨论】:
已经知道它是如何工作的。我的问题是关于方法的正确性还是更好的方法?我相信,没有什么能阻止他们(如我所见)在惰性计算中实现正确的异常处理。 我的意思是,他们可以为 isFailure 提供更多信息(至少)或记住异常(最多,但更昂贵)。目前,0
表示失败/未初始化,因此您无法区分它们。
我不能说清楚,但可能是出于性能原因 (twitter.com/djspiewak/status/302489756552536064)。对我来说,这两种方法似乎都是合理的。虽然不完全相同,但根据您的用例,您可以通过 lazy val aaa = Try println("calc"); sys.error("aaaa")
模拟您的方法
甚至更好:lazy val _aaa = Try println("calc"); sys.error("aaaa") ; lazy val aaa = _aaa.get
@kosii _aaa
是 Success
或 Failure
。如果成功了,那么get
没有副作用,可以多次调用。如果失败,则get
抛出异常,因此lazy
无论如何都不会缓存它。如我所见,将aaa
设为lazy val
不会在语义上添加任何内容,不是吗?而且lazy val
比def
贵。以上是关于`lazy val` 在异常情况下表现得像 `def` 是正确的行为吗?的主要内容,如果未能解决你的问题,请参考以下文章
当它应该表现得像 try/catch/finally 时,为啥使用会抛出异常?