首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Scala中的var和val定义有什么不同?

Scala中的var和val定义有什么不同?
EN

Stack Overflow用户
提问于 2009-11-25 00:57:39
回答 12查看 119.5K关注 0票数 319

Scala语言中的varval定义有什么不同?为什么语言需要两者?为什么要选择val而不是var,反之亦然?

EN

回答 12

Stack Overflow用户

回答已采纳

发布于 2009-11-25 03:02:25

正如许多人所说的那样,分配给val的对象是不可替换的,而分配给var的对象是可以替换的。然而,所述对象可以修改其内部状态。例如:

代码语言:javascript
运行
复制
class A(n: Int) {
  var value = n
}

class B(n: Int) {
  val value = new A(n)
}

object Test {
  def main(args: Array[String]) {
    val x = new B(5)
    x = new B(6) // Doesn't work, because I can't replace the object created on the line above with this new one.
    x.value = new A(6) // Doesn't work, because I can't replace the object assigned to B.value for a new one.
    x.value.value = 6 // Works, because A.value can receive a new object.
  }
}

因此,即使我们不能更改分配给x的对象,我们也可以更改该对象的状态。然而,在它的根部,有一个var

现在,由于许多原因,不变性是一件好事。首先,如果一个对象没有改变内部状态,你就不必担心代码的其他部分会不会改变它。例如:

代码语言:javascript
运行
复制
x = new B(0)
f(x)
if (x.value.value == 0)
  println("f didn't do anything to x")
else
  println("f did something to x")

这在多线程系统中变得尤为重要。在多线程系统中,可能会发生以下情况:

代码语言:javascript
运行
复制
x = new B(1)
f(x)
if (x.value.value == 1) {
  print(x.value.value) // Can be different than 1!
}

如果您只使用val,并且只使用不可变的数据结构(即避免使用数组、scala.collection.mutable中的所有内容等),那么您可以放心,这种情况不会发生。也就是说,除非有一些代码,甚至可能是一个框架,否则会使用反射技巧--不幸的是,反射可能会改变“不可变”的值。

这是一个原因,但还有另一个原因。当您使用var时,您可能会出于多种目的重用相同的var。这有一些问题:

  • 阅读代码的人将更难知道代码的某个部分中变量的值是什么。
  • 您可能会忘记重新初始化某些代码路径中的变量,最终会在代码下游传递错误的值。

简而言之,使用val更安全,代码更具可读性。

然后,我们可以走另一个方向。如果val更好,那为什么还要有var呢?嗯,有些语言确实采用了这种方法,但在某些情况下,可变性可以极大地提高性能。

例如,取一个不可变的Queue。当你在其中enqueuedequeue对象时,你会得到一个新的Queue对象。那么,您将如何处理其中的所有项呢?

我将通过一个示例进行说明。假设你有一个数字队列,你想用它们组成一个数字。例如,如果我有一个2,1,3的队列,按照这个顺序,我想取回数字213。让我们首先用mutable.Queue来解决这个问题

代码语言:javascript
运行
复制
def toNum(q: scala.collection.mutable.Queue[Int]) = {
  var num = 0
  while (!q.isEmpty) {
    num *= 10
    num += q.dequeue
  }
  num
}

这段代码快速且易于理解。它的主要缺点是传递的队列是由toNum修改的,因此您必须事先对其进行复制。这就是不变性使你摆脱的那种对象管理。

现在,让我们将其转换为immutable.Queue

代码语言:javascript
运行
复制
def toNum(q: scala.collection.immutable.Queue[Int]) = {
  def recurse(qr: scala.collection.immutable.Queue[Int], num: Int): Int = {
    if (qr.isEmpty)
      num
    else {
      val (digit, newQ) = qr.dequeue
      recurse(newQ, num * 10 + digit)
    }
  }
  recurse(q, 0)
}

因为我不能重用一些变量来跟踪我的num,就像前面的例子一样,我需要求助于递归。在这种情况下,它是尾递归,具有相当好的性能。但情况并不总是这样:有时就是没有好的(可读的,简单的)尾递归解决方案。

但是请注意,我可以重写该代码以同时使用immutable.Queuevar!例如:

代码语言:javascript
运行
复制
def toNum(q: scala.collection.immutable.Queue[Int]) = {
  var qr = q
  var num = 0
  while (!qr.isEmpty) {
    val (digit, newQ) = qr.dequeue
    num *= 10
    num += digit
    qr = newQ
  }
  num
}

这段代码仍然是高效的,不需要递归,而且在调用toNum之前,您不需要担心是否必须复制队列。自然,我避免将变量重用于其他目的,并且此函数外部的代码都看不到它们,因此我不需要担心它们的值从一行更改到下一行--除非我显式地这样做。

Scala选择让程序员这样做,如果程序员认为这是最好的解决方案。其他语言已经选择使这类代码变得困难。Scala (以及任何具有广泛可变性的语言)付出的代价是,编译器在优化代码方面没有足够的回旋余地。Java的解决方案是基于运行时配置文件优化代码。我们可以继续讨论每一方的利弊。

就我个人而言,我认为Scala目前取得了正确的平衡。到目前为止,它并不完美。我认为ClojureHaskell都有一些非常有趣的概念没有被Scala采用,但是Scala也有自己的优势。让我们看看未来会发生什么。

票数 350
EN

Stack Overflow用户

发布于 2009-11-25 00:58:43

val是最终的,也就是说,无法设置。想想java中的final吧。

票数 62
EN

Stack Overflow用户

发布于 2015-08-20 12:18:08

简而言之:

var = variable

val =v变量+ final

票数 59
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1791408

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档