Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/kubernetes/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/vim/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 选择def而不是val的利弊_Scala_Lazy Evaluation - Fatal编程技术网

Scala 选择def而不是val的利弊

Scala 选择def而不是val的利弊,scala,lazy-evaluation,Scala,Lazy Evaluation,我问的问题和你的略有不同。假设我有一个代码片段: 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 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?

没有语义差异,这对我是否有重大的不利影响。如果“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更灵活

编辑:在val上使用def的另一个用例是当一个抽象类有一个“val”时,该值应该由一个子类提供

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

val是严格的,一旦定义了对象,就会给它一个值


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

我可以看到一个优势,即使用
def
时,与使用
val
时相比,您更少地绑定到某个位置

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

所以,愚蠢的例子(如果你有更好的答案,请编辑这个答案),这在
val
中是不可能的:

def foo(i : Int) : List[String] = {
  def ret = s :: Nil
  def s = i.toString + "!"
  ret
}
在某些情况下,这可能很重要,也可能很方便

(因此,基本上,您可以使用
lazy val
实现同样的效果,但是,如果最多调用一次,它可能会比
lazy val
更快)

1)

我没有看到提到的一个答案是,您所描述的方法的堆栈框架实际上可能更小。您声明的每个
val
都将占用JVM堆栈上的一个插槽,但是,每当您使用
def
获得的值时,它将在您使用它的第一个表达式中被使用。即使
def
引用了环境中的某些内容,编译器也会通过。 热点应该优化这两个方面,或者一些人声称。见:

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

但是,请恕我直言——我实际上还没有制定大量的基准来支持这一说法

(二)

此外,为了扩展Kevin的有效回复,稳定的
val
还意味着您可以使用它,这是
def
无法使用的,因为编译器不会检查它的纯度

(三)

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


本质上,使用
def
s生成
Streams
可以确保不存在对这些对象的额外引用,这对GC很重要。由于
Stream
s无论如何都是懒惰的,因此即使您有多个
def
s,创建它们的开销也可能可以忽略不计。

def
将被“提升”到“outer”类中的一个方法,而val将只是一个标准值,但如果我只使用一次,并且它完全在一个方法中,这并不像并发访问那样,对吧?我知道这两件事之间的区别是什么,我感兴趣的是在使用
def
的辩护中可以编组哪些其他参数,val只是一个def(标记为稳定)加上一个支持字段。如果我知道我总是在某些子类中提供支持(例如父类是抽象的),那么我倾向于使用def。这样做没有特别强的逻辑,只是直觉上认为val是该方法的一个特例具体实现。。。对于内部val,我知道我不会对其进行子类化,因此我只会在不需要昂贵计算的情况下使用def或lazy val。但是如果只使用一次,lazy val将比def慢。这是真的-但我预计过度使用def将再次导致大量类膨胀,这同样适用于
lazy val
s。因此优势仍然存在。两者都将使用方法作为访问器,因此请这样考虑:如果想要方法,请使用def;如果想要值成员,请使用val;如果想要非严格值成员,请使用lazy val;如果想要变量成员,请使用var。在我的示例中,
def
不能被重写,因为它是在方法中声明的