我问的是一个略有不同的问题 这个。假设我有一个代码片段:
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
小号 def
S'
1)
我没有看到的一个答案是你所描述的方法的堆栈框架实际上可能更小。每 val
你声明将占用JVM堆栈上的一个插槽,但是,只要你使用一个 def
获得的值将在你使用它的第一个表达式中消耗掉。即使是 def
引用来自环境的东西,编译器将通过。
HotSpot应该优化这些东西,或者有些人声称。看到:
http://www.ibm.com/developerworks/library/j-jtp12214/
由于内部方法被编译为场景后面的常规私有方法并且通常非常小,因此JIT编译器可能会选择内联它然后对其进行优化。这可以节省分配较小堆栈帧的时间(?),或者通过在堆栈上使用较少的元素,使局部变量访问更快。
但是,拿这个(大)盐 - 我实际上没有做出广泛的基准来备份这个说法。
2)
另外,要扩大凯文的有效回复,稳定 val
提供也意味着您可以使用它 路径依赖类型 - 你不能做的事情 def
,因为编译器不检查其纯度。
3)
出于另一个原因,您可能想要使用 def
,看到不久前问过的一个相关问题:
Scala流的功能处理没有OutOfMemory错误
基本上,使用 def
生产 Streams
确保不存在对这些对象的附加引用,这对GC很重要。以来 Stream
无论如何,s是懒惰的,即使你有多个,创建它们的开销也许可以忽略不计 def
秒。
val是严格的,只要你定义了它就给它一个值。
在内部,编译器会将其标记为STABLE,相当于Java中的final。这个 应该 允许JVM进行各种优化 - 我只是不知道它们是什么:)
我可以看到一个优点,即你在使用时不那么受某个位置的束缚 def
比使用时 val
。
这不是技术优势,但在某些情况下允许更好的结构。
所以,愚蠢的例子(请编辑这个答案,如果你有一个更好的答案),这是不可能的 val
:
def foo(i : Int) : List[String] = {
def ret = s :: Nil
def s = i.toString + "!"
ret
}
可能存在重要或方便的情况。
(所以,基本上,你可以实现同样的目标 lazy val
但是,如果最多只调用一次,它可能会比a更快 lazy val
。)
对于像这样的局部声明(没有参数,精确计算一次并且在声明点和评估点之间没有评估代码),没有语义差异。如果“val”版本编译为比“def”版本更简单,更高效的代码,我不会感到惊讶,但你必须检查字节码和可能的配置文件。
在你的例子中,我会使用一个 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(...) }