选择def val的优点和缺点

时间:2010-11-09 12:09:21

标签: scala lazy-evaluation

我问的问题与this one略有不同。假设我有一个代码段:

def foo(i : Int) : List[String] = {
  val s = i.toString + "!" //using val
  s :: Nil
}

这在功能上等同于以下内容:

def foo(i : Int) : List[String] = {
  def s = i.toString + "!" //using def
  s :: Nil
}

为什么我会选择一个而不是另一个?显然我会假设第二个在以下方面有一些缺点:

  • 创建更多字节码(内部def被提升到类中的方法)
  • 调用方法而不是访问值的运行时性能开销
  • 非严格评估意味着我可以轻松访问s两次(即不必要地重做计算)

我能想到的唯一优势是:

  • s的非严格评估意味着只有在使用它时才调用它(但我可以使用lazy val

这里的人们的想法是什么?在制作所有内部val s def s?

时,是否存在重大的不利益处?

5 个答案:

答案 0 :(得分:6)

1)

我没有看到的一个答案是你所描述的方法的堆栈框架实际上可能更小。您声明的每个val将占用JVM堆栈上的一个插槽,但是,无论何时使用def获取的值,它将在您使用它的第一个表达式中消耗。即使{{1从环境中引用一些东西,编译器就会传递。 HotSpot应该优化这些东西,或者有些人声称。参见:

http://www.ibm.com/developerworks/library/j-jtp12214/

由于内部方法被编译为场景后面的常规私有方法,并且通常非常小,因此JIT编译器可能会选择内联它然后对其进行优化。这可以节省分配较小堆栈帧的时间(?),或者通过在堆栈上使用较少的元素,使局部变量访问更快。

但是,考虑到这个(大)盐 - 我实际上没有做出广泛的基准来备份这个说法。

2)

此外,为了扩展Kevin的有效回复,稳定的def也意味着您可以将其与path dependent types一起使用 - 这是val无法做到的事情,因为编译器不检查其纯度。

3)

出于另一个原因,您可能想要使用def,请参阅不久前提出的相关问题:

Functional processing of Scala streams without OutOfMemory errors

基本上,使用def生成def可确保不存在对这些对象的其他引用,这对GC很重要。由于Streams无论如何都是懒惰的,即使你有多个Stream,创建它们的开销也可能微不足道。

答案 1 :(得分:3)

val是严格的,只要你定义了它就给它一个值。

在内部,编译器会将其标记为STABLE,相当于Java中的final。这个应该允许JVM进行各种优化 - 我只是不知道它们是什么:)

答案 2 :(得分:2)

我可以看到,使用def时与使用val相比,您不太适合某个位置。

这不是技术优势,但在某些情况下允许更好的结构化。

所以,愚蠢的例子(请编辑这个答案,如果你有一个更好的答案),val无法做到这一点:

def foo(i : Int) : List[String] = {
  def ret = s :: Nil
  def s = i.toString + "!"
  ret
}

可能存在重要或方便的情况。

(所以,基本上,您可以使用lazy val实现相同的目标,但是,如果最多只调用一次,则可能会比lazy val更快。)

答案 3 :(得分:0)

对于这样的局部声明(没有参数,精确计算一次,并且在声明点和评估点之间没有评估代码),没有语义差异。如果“val”版本编译为比“def”版本更简单,更高效的代码,我不会感到惊讶,但你必须检查字节码和可能的配置文件。

答案 4 :(得分:0)

在您的示例中,我将使用val。我认为在声明类成员时val / def的选择更有意义:

class A { def a0 = "a"; def a1 = "a" }

class B extends A {
  var c = 0
  override def a0 = { c += 1; "a" + c }
  override val a1 = "b" 
}

在使用def的基类中,子类可能会覆盖不返回常量的def。或者它可以用val覆盖。因此,它比val更具灵活性。

编辑:使用def over val的另一个用例是抽象类具有“val”,其值应由子类提供。

abstract class C { def f: SomeObject }
new C { val f = new SomeObject(...) }