Scala语言中的var和val定义有什么不同?为什么语言需要两者?为什么要选择val而不是var,反之亦然?
发布于 2009-11-25 03:02:25
正如许多人所说的那样,分配给val的对象是不可替换的,而分配给var的对象是可以替换的。然而,所述对象可以修改其内部状态。例如:
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。
现在,由于许多原因,不变性是一件好事。首先,如果一个对象没有改变内部状态,你就不必担心代码的其他部分会不会改变它。例如:
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")这在多线程系统中变得尤为重要。在多线程系统中,可能会发生以下情况:
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。当你在其中enqueue或dequeue对象时,你会得到一个新的Queue对象。那么,您将如何处理其中的所有项呢?
我将通过一个示例进行说明。假设你有一个数字队列,你想用它们组成一个数字。例如,如果我有一个2,1,3的队列,按照这个顺序,我想取回数字213。让我们首先用mutable.Queue来解决这个问题
def toNum(q: scala.collection.mutable.Queue[Int]) = {
var num = 0
while (!q.isEmpty) {
num *= 10
num += q.dequeue
}
num
}这段代码快速且易于理解。它的主要缺点是传递的队列是由toNum修改的,因此您必须事先对其进行复制。这就是不变性使你摆脱的那种对象管理。
现在,让我们将其转换为immutable.Queue
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.Queue和var!例如:
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目前取得了正确的平衡。到目前为止,它并不完美。我认为Clojure和Haskell都有一些非常有趣的概念没有被Scala采用,但是Scala也有自己的优势。让我们看看未来会发生什么。
发布于 2009-11-25 00:58:43
val是最终的,也就是说,无法设置。想想java中的final吧。
发布于 2015-08-20 12:18:08
简而言之:
var = variable
val =v变量+ final
https://stackoverflow.com/questions/1791408
复制相似问题