我理解正确吗
- 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 valvs的想法val。另一个问题是lazy valVS 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
选择defover的一个很好的理由val,尤其是在抽象类(或用于模仿Java接口的特征)中,可以def用a覆盖avalin子类,但是不能反过来。
关于lazy,我可以看到有两件事应该牢记。首先是lazy引入了一些运行时开销,但是我想您需要对特定情况进行基准测试,以了解这是否确实对运行时性能产生重大影响。另一个问题lazy是,它可能会延迟引发异常,这可能会使推理程序变得更困难,因为异常不是预先抛出而是仅在首次使用时抛出。
firthElement不是一个很好的例子来说明它。如果您考虑前置条件和后置条件,则seq(5)只有在序列包含至少六个元素的情况下,调用才显然是安全的-即使isEmpty是假,也不能保证。更糟糕的是,如果调用方fithElement不是最初创建对象的调用方,则它的义务是无法实现的,因为调用方缺乏对序列长度的意识。添加length函数将使您的示例有效。