何时在Scala中使用延迟值?

时间:2013-11-13 14:14:32

标签: java scala jvm

为什么Scala会引入延迟参数。它不应该由JVM管理(对于用户是不可见的)如何初始化值?什么是真实世界的用例,值得将控件交给开发人员并将值定义为惰性?

4 个答案:

答案 0 :(得分:8)

一种常见的情况是,类的编写者不知道是否会使用昂贵的初始化val。在这种情况下,val会根据需要进行初始化。

另一种情况是有机控制初始化的排序。通常,在初始化特定val之前很久就会创建一个对象,因为其他类尚未初始化。在这种情况下,懒惰提供了一种方便的方法,可以自然地进行这种测序,而无需作者提出一个对复杂的多相初始化进行排序的主计划。

答案 1 :(得分:8)

名字参数:主要动机之一是支持dsls。它们允许您在API中使用非常好的语法,几乎感觉它们就像是内置于语言中一样。例如,您可以非常轻松地定义自己的自定义repeat - 循环:

def repeat(body: =>Unit)(until: =>Boolean): Unit = {
  body
  if (until) {} else repeat(body)(until)
}

然后使用它,好像它是语言的一部分。

var i = 0
repeat {
  println(i)
  i += 1
} (i < 3)

或者您可以类似地生成这样的新主题:spawn { println("on the new thread!") },或者您可以对FileInputStream进行自动资源管理,如下所示:withFile("/home/john/.bashrc") { println(_.contents) }

lazy - 这里的动机是:

  1. Streams这样的懒惰数据结构,在函数式语言中很流行,可用于实现高效的数据结构a-la Okasaki的功能队列。
  2. 避免分配或初始化一些昂贵的资源,如果它们从未在某些对象中使​​用过,例如文件句柄或数据库连接。
  3. 初始化objects fields in the correct order,用于由许多mixins组成的对象。
  4. 当有许多线程共享一个值时,
  5. 实现正确的“仅初始化一次”语义(参见introduction here)。
  6. 有嵌套单例对象的翻译方案:
  7. class A { object B }

    变得像:

    class A {
      class A$B$
      lazy val B = new A$B$
    }
    

答案 2 :(得分:4)

TLDR:因为出于性能原因导致用户失败

今天的大多数语言都是渴望。其中一些不是,他们称为懒惰。虽然许多编程问题可以通过懒惰评估以美观和简洁的方式表达,但我认为绝对懒惰不是一个好主意。从主观的角度来看,程序员习惯于以一种急切的方式思考(特别是那些来自命令性领域的人),所以在Haskell中天真地编写程序可能会让你感到困惑。对于每个可能的菜肴只有叉子不如在叉子和勺子之间做出选择那么好,虽然scala支持语言级别的懒惰评估,但它默认为渴望模型。原因(除了马丁和其他语言设计师的个人选择)是Java和Scala之间的互操作 - 用一种语言组成这两个世界将是一场噩梦。此外,在Scala设计时,JVM还没有支持这样的功能,只有在Java 7中引入方法句柄才能实现more or less performant lazy vals(仅仅两年前,scala已经存在了十年)。

答案 3 :(得分:3)

我会回答我自己的问题。因此,当惰性值非常有用时,一个用例是如果要创建具有循环的不可变数据结构。没有懒惰,什么是不容易的,否则你将不得不修改已经创建的对象。如果您希望对象是不可变的,则无法进行此操作。让我以简单循环实现为例。

enter image description here

因此,在Scala中,您可以通过以下方式实现此目的

class Node(inNode: => Node) { lazy val in = inNode }

lazy val node :Node = new Node(new Node(node))

这样您就创建了一个不可变循环。您可以通过比较参考来验证结果。

scala> node.in
res3: Node = Node@2d928643

scala> node.in.in
res4: Node = Node@3a5ed7a6

scala> node
res5: Node = Node@3a5ed7a6