本文翻译自:What is the difference between a var and val definition in Scala?
What is the difference between a var
and val
definition in Scala and why does the language need both? Scala中var
和val
定义之间有什么区别?为什么语言需要两者? Why would you choose a val
over a var
and vice versa? 为什么你会选择val
而不是var
,反之亦然?
#1楼
参考:https://stackoom.com/question/7w1G/Scala中var和val定义有什么区别
#2楼
val
means immutable and var
means mutable. val
表示不可变, var
表示可变。
#3楼
val
is final, that is, cannot be set. val
是final,也就是说,无法设置。 Think final
in java. 在java中思考final
。
#4楼
The difference is that a var
can be re-assigned to whereas a val
cannot. 不同之处在于var
可以重新分配,而val
则不能。 The mutability, or otherwise of whatever is actually assigned, is a side issue: 任何实际分配的可变性或其他方面都是一个副作用:
import collection.immutable
import collection.mutable
var m = immutable.Set("London", "Paris")
m = immutable.Set("New York") //Reassignment - I have change the "value" at m.
Whereas: 鉴于:
val n = immutable.Set("London", "Paris")
n = immutable.Set("New York") //Will not compile as n is a val.
And hence: 因此:
val n = mutable.Set("London", "Paris")
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable.
If you are building a data structure and all of its fields are val
s, then that data structure is therefore immutable, as its state cannot change. 如果要构建数据结构并且其所有字段都是val
,那么该数据结构因此是不可变的,因为其状态不能更改。
#5楼
"val means immutable and var means mutable." “val表示不可变,var表示可变。”
To paraphrase, "val means value and var means variable". 换句话说,“val表示值,var表示变量”。
A distinction that happens to be extremely important in computing (because those two concepts define the very essence of what programming is all about), and that OO has managed to blur almost completely, because in OO, the only axiom is that "everything is an object". 在计算中恰好非常重要的区别(因为这两个概念定义了编程的本质),并且OO几乎完全模糊了,因为在OO中,唯一的公理是“一切都是宾语”。 And that as a consequence, lots of programmers these days tend not to understand/appreciate/recognize, because they have been brainwashed into "thinking the OO way" exclusively. 因此,如今许多程序员往往不理解/欣赏/认识,因为他们已被洗脑成“专注于OO方式”。 Often leading to variable/mutable objects being used like everywhere , when value/immutable objects might/would often have been better. 当值/不可变对象可能/通常会更好时,通常会导致变量/可变对象像所有地方一样被使用。
#6楼
As so many others have said, the object assigned to a val
cannot be replaced, and the object assigned to a var
can. 正如许多其他人所说,分配给val
的对象不能被替换,并且分配给var
的对象可以。 However, said object can have its internal state modified. 但是,所述对象可以修改其内部状态。 For example: 例如:
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.
}
}
So, even though we can't change the object assigned to x
, we could change the state of that object. 因此,即使我们无法更改分配给x
的对象,我们也可以更改该对象的状态。 At the root of it, however, there was a var
. 然而,在它的根源,有一个var
。
Now, immutability is a good thing for many reasons. 现在,由于许多原因,不变性是一件好事。 First, if an object doesn't change internal state, you don't have to worry if some other part of your code is changing it. 首先,如果一个对象没有改变内部状态,你不必担心代码的其他部分是否正在改变它。 For example: 例如:
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")
This becomes particularly important with multithreaded systems. 对于多线程系统,这一点尤为重要。 In a multithreaded system, the following can happen: 在多线程系统中,可能发生以下情况:
x = new B(1)
f(x)
if (x.value.value == 1) {
print(x.value.value) // Can be different than 1!
}
If you use val
exclusively, and only use immutable data structures (that is, avoid arrays, everything in scala.collection.mutable
, etc.), you can rest assured this won't happen. 如果你只使用val
,并且只使用不可变数据结构(即避免数组, scala.collection.mutable
所有内容等),你可以放心,这不会发生。 That is, unless there's some code, perhaps even a framework, doing reflection tricks -- reflection can change "immutable" values, unfortunately. 也就是说,除非有一些代码,甚至是一个框架,做反射技巧 - 不幸的是,反射可以改变“不可变”值。
That's one reason, but there is another reason for it. 这是一个原因,但还有另一个原因。 When you use var
, you can be tempted into reusing the same var
for multiple purposes. 当您使用var
,您可能会尝试重复使用相同的var
用于多种目的。 This has some problems: 这有一些问题:
- It will be more difficult for people reading the code to know what is the value of a variable in a certain part of the code. 阅读代码的人更难以知道代码的某个部分中变量的值是什么。
- You may forget to re-initialize the variable in some code path, and end up passing wrong values downstream in the code. 您可能忘记在某些代码路径中重新初始化变量,并最终在代码中向下游传递错误的值。
Simply put, using val
is safer and leads to more readable code. 简单地说,使用val
更安全并且导致更易读的代码。
We can, then, go the other direction. 那么,我们可以走向另一个方向。 If val
is that better, why have var
at all? 如果val
更好,为什么要有var
? Well, some languages did take that route, but there are situations in which mutability improves performance, a lot. 好吧,有些语言确实采用了这种方式,但在某些情况下,可变性可以提高性能。
For example, take an immutable Queue
. 例如,采用不可变的Queue
。 When you either enqueue
or dequeue
things in it, you get a new Queue
object. 当您将其中的内容enqueue
或dequeue
列时,您将获得一个新的Queue
对象。 How then, would you go about processing all items in it? 那你怎么去处理它里面的所有物品呢?
I'll go through that with an example. 我将以一个例子来说明这一点。 Let's say you have a queue of digits, and you want to compose a number out of them. 假设您有一个数字队列,并且您想要从中编写一个数字。 For example, if I have a queue with 2, 1, 3, in that order, I want to get back the number 213. Let's first solve it with a mutable.Queue
: 例如,如果我有一个mutable.Queue
的队列,那么我想要取回213号。让我们先用mutable.Queue
解决它:
def toNum(q: scala.collection.mutable.Queue[Int]) = {
var num = 0
while (!q.isEmpty) {
num *= 10
num += q.dequeue
}
num
}
This code is fast and easy to understand. 此代码快速且易于理解。 Its main drawback is that the queue that is passed is modified by toNum
, so you have to make a copy of it beforehand. 它的主要缺点是传递的队列由toNum
修改,因此您必须事先复制它。 That's the kind of object management that immutability makes you free from. 这就是不变性让你摆脱的对象管理。
Now, let's covert it to an immutable.Queue
: 现在,让我们把它转换成一个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)
}
Because I can't reuse some variable to keep track of my num
, like in the previous example, I need to resort to recursion. 因为我不能重用一些变量来跟踪我的num
,就像前面的例子一样,我需要求助于递归。 In this case, it is a tail-recursion, which has pretty good performance. 在这种情况下,它是一个尾递归,具有相当不错的性能。 But that is not always the case: sometimes there is just no good (readable, simple) tail recursion solution. 但情况并非总是如此:有时候没有好的(可读的,简单的)尾递归解决方案。
Note, however, that I can rewrite that code to use an immutable.Queue
and a var
at the same time! 但请注意,我可以重写该代码以同时使用immutable.Queue
和var
! For example: 例如:
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
}
This code is still efficient, does not require recursion, and you don't need to worry whether you have to make a copy of your queue or not before calling toNum
. 此代码仍然有效,不需要递归,您无需担心在调用toNum
之前是否必须复制队列。 Naturally, I avoided reusing variables for other purposes, and no code outside this function sees them, so I don't need to worry about their values changing from one line to the next -- except when I explicitly do so. 当然,我避免将变量重用于其他目的,并且此函数之外的代码都没有看到它们,因此我不需要担心它们的值从一行变为下一行 - 除非我明确这样做。
Scala opted to let the programmer do that, if the programmer deemed it to be the best solution. 如果程序员认为程序员认为它是最好的解决方案,Scala选择让程序员这样做。 Other languages have chosen to make such code difficult. 其他语言选择使这些代码变得困难。 The price Scala (and any language with widespread mutability) pays is that the compiler doesn't have as much leeway in optimizing the code as it could otherwise. Scala(以及任何具有广泛可变性的语言)付出的代价是编译器在优化代码方面没有那么多的余地。 Java's answer to that is optimizing the code based on the run-time profile. Java的答案是根据运行时配置文件优化代码。 We could go on and on about pros and cons to each side. 我们可以继续讨论各方的利弊。
Personally, I think Scala strikes the right balance, for now. 就个人而言,我认为斯卡拉目前正在取得适当的平衡。 It is not perfect, by far. 到目前为止,它并不完美。 I think both Clojure and Haskell have very interesting notions not adopted by Scala, but Scala has its own strengths as well. 我认为Clojure和Haskell都有非常有趣的概念,Scala没有采用,但Scala也有自己的优势。 We'll see what comes up on the future. 我们将看到未来会发生什么。