我注意到Scala提供了惰性val。但我不明白他们是干什么的。

scala> val x = 15
x: Int = 15

scala> lazy val y = 13
y: Int = <lazy>

scala> x
res0: Int = 15

scala> y
res1: Int = 13

REPL显示y是一个惰性值,但它与正常值有何不同?


当前回答

scala> lazy val lazyEight = {
     |   println("I am lazy !")
     |   8
     | }
lazyEight: Int = <lazy>

scala> lazyEight
I am lazy !
res1: Int = 8

所有val都在对象构造过程中初始化 使用lazy关键字将初始化推迟到第一次使用 注意:惰性值不是最终值,因此可能会显示性能缺陷

其他回答

lazy在没有循环依赖的情况下也很有用,如下所示:

abstract class X {
  val x: String
  println ("x is "+x.length)
}

object Y extends X { val x = "Hello" }
Y

现在访问Y会抛出空指针异常,因为x还没有初始化。 但是,下面的方法可以很好地工作:

abstract class X {
  val x: String
  println ("x is "+x.length)
}

object Y extends X { lazy val x = "Hello" }
Y

编辑:以下也可以:

object Y extends { val x = "Hello" } with X 

这被称为“早期初始化式”。更多细节请参见这个SO问题。

惰性的演示-如上所定义-定义时执行vs访问时执行:(使用2.12.7 scala shell)

// compiler says this is ok when it is lazy
scala> lazy val t: Int = t 
t: Int = <lazy>
//however when executed, t recursively calls itself, and causes a StackOverflowError
scala> t             
java.lang.StackOverflowError
...

// when the t is initialized to itself un-lazily, the compiler warns you of the recursive call
scala> val t: Int = t
<console>:12: warning: value t does nothing other than call itself recursively
   val t: Int = t

我知道答案已经给出了,但我写了一个简单的例子,让像我这样的初学者容易理解:

var x = { println("x"); 15 }
lazy val y = { println("y"); x + 1 }
println("-----")
x = 17
println("y is: " + y)

以上代码的输出为:

x
-----
y
y is: 18

可以看到,x在初始化时被打印出来,但y在以同样的方式初始化时没有被打印出来(这里我故意将x作为var -以解释y何时被初始化)。接下来,当y被调用时,它被初始化,并且最后一个'x'的值被考虑,而不是旧的。

希望这能有所帮助。

scala> lazy val lazyEight = {
     |   println("I am lazy !")
     |   8
     | }
lazyEight: Int = <lazy>

scala> lazyEight
I am lazy !
res1: Int = 8

所有val都在对象构造过程中初始化 使用lazy关键字将初始化推迟到第一次使用 注意:惰性值不是最终值,因此可能会显示性能缺陷

这个特性不仅有助于延迟昂贵的计算,而且对于构造相互依赖或循环结构也很有用。例如,这会导致堆栈溢出:

trait Foo { val foo: Foo }
case class Fee extends Foo { val foo = Faa() }
case class Faa extends Foo { val foo = Fee() }

println(Fee().foo)
//StackOverflowException

但是对于lazy vals,它工作得很好

trait Foo { val foo: Foo }
case class Fee extends Foo { lazy val foo = Faa() }
case class Faa extends Foo { lazy val foo = Fee() }

println(Fee().foo)
//Faa()