我理解正确吗
def
每次访问时都会进行评估lazy val
一旦被访问就被评估val
一旦进入执行范围就被评估?
我理解正确吗
def
每次访问时都会进行评估
lazy val
一旦被访问就被评估
val
一旦进入执行范围就被评估?
val
不能随意扩展。如果您在基本特征中具有val a = 5
,val b = a + 1
并尝试使用扩展它,val a = 6
那么b
将引发一些未定义的错误,而不是7 。
Answers:
是的,但是有一个不错的窍门:如果您的值比较懒惰,并且在第一次评估时会出现异常,那么下次您尝试访问它时,它将尝试重新评估自己。
这是示例:
scala> import io.Source
import io.Source
scala> class Test {
| lazy val foo = Source.fromFile("./bar.txt").getLines
| }
defined class Test
scala> val baz = new Test
baz: Test = Test@ea5d87
//right now there is no bar.txt
scala> baz.foo
java.io.FileNotFoundException: ./bar.txt (No such file or directory)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:137)
...
// now I've created empty file named bar.txt
// class instance is the same
scala> baz.foo
res2: Iterator[String] = empty iterator
lazy val
vs的想法val
。另一个问题是lazy val
VS def
。我有一个不可变的类,其中的某些属性是构造函数中传递的数据的纯函数。将它们公开为惰性val而不是defs是不是一个好主意(考虑结果是一小部分数据(如整数值),但需要一些时间来计算)?
我想通过我在REPL中执行的示例来解释这些差异,我相信这个简单的示例更容易掌握并解释概念上的差异。
在这里,我正在创建一个val result1,一个懒惰的val result2和一个def result3,它们每个都有一个String类型。
一种)。值
scala> val result1 = {println("hello val"); "returns val"}
hello val
result1: String = returns val
在这里,执行println的原因是在这里已计算出result1的值。因此,现在result1将始终引用其值,即“ returns val”。
scala> result1
res0: String = returns val
因此,现在,您可以看到result1现在引用了它的值。请注意,这里不会执行println语句,因为result1的值是在第一次执行时已经计算出的。因此,从现在开始,result1将始终返回相同的值,并且将不再执行println语句,因为已经执行了用于获取result1的值的计算。
B)。懒惰的瓦尔
scala> lazy val result2 = {println("hello lazy val"); "returns lazy val"}
result2: String = <lazy>
正如我们在这里看到的,这里不执行println语句,也没有计算值。这就是懒惰的本质。
现在,当我第一次引用result2时,将执行println语句并计算和分配值。
scala> result2
hello lazy val
res1: String = returns lazy val
现在,当我再次引用result2时,这一次,我们将只看到它保存的值,并且不会执行println语句。从现在开始,result2的行为就像val一样,并始终返回其缓存值。
scala> result2
res2: String = returns lazy val
C)。定义
如果是def,则每次调用result3时都必须计算结果。这也是我们在scala中将方法定义为def的主要原因,因为方法必须在每次在程序内部调用时都要计算并返回一个值。
scala> def result3 = {println("hello def"); "returns def"}
result3: String
scala> result3
hello def
res3: String = returns def
scala> result3
hello def
res4: String = returns def
选择def
over的一个很好的理由val
,尤其是在抽象类(或用于模仿Java接口的特征)中,可以def
用a覆盖aval
in子类,但是不能反过来。
关于lazy
,我可以看到有两件事应该牢记。首先是lazy
引入了一些运行时开销,但是我想您需要对特定情况进行基准测试,以了解这是否确实对运行时性能产生重大影响。另一个问题lazy
是,它可能会延迟引发异常,这可能会使推理程序变得更困难,因为异常不是预先抛出而是仅在首次使用时抛出。
firthElement
不是一个很好的例子来说明它。如果您考虑前置条件和后置条件,则seq(5)
只有在序列包含至少六个元素的情况下,调用才显然是安全的-即使isEmpty
是假,也不能保证。更糟糕的是,如果调用方fithElement
不是最初创建对象的调用方,则它的义务是无法实现的,因为调用方缺乏对序列长度的意识。添加length
函数将使您的示例有效。