选择def val的优点和缺点

oxb*_*kes 11 scala lazy-evaluation

我问的问题与这个问题略有不同.假设我有一个代码段:

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

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

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

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

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

我能想到的唯一优势是:

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

人们的想法在这里是什么?制作所有内心val的东西对我来说是否有重大的不利影响def

axe*_*l22 6

1)

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

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

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

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

2)

另外,为了扩展Kevin的有效回复,stable val提供也意味着你可以将它与路径依赖类型一起使用 - 这是你无法做到的def,因为编译器不会检查它的纯度.

3)

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

Scala流的功能处理没有OutOfMemory错误

基本上,使用defs生成Streams可确保不存在对这些对象的其他引用,这对GC很重要.因为Streams无论如何都是懒惰的,即使你有多个defs ,创建它们的开销也许可以忽略不计.


Dav*_*ith 0

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