我问的问题与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?
答案 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(...) }