Scala中var和val定义的区别是什么?

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

126802 次浏览

val表示不可变,var表示可变。

完整讨论

val是final的,也就是说,不能被设置。想想java中的final

区别在于var可以被重新赋值,而val则不能。可变性,或其他任何实际分配的东西,是一个次要问题:

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.

而:

val n = immutable.Set("London", "Paris")
n = immutable.Set("New York") //Will not compile as n is a val.

因此:

val n = mutable.Set("London", "Paris")
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable.

如果你正在构建一个数据结构,它的所有字段都是__abc0,那么这个数据结构因此是不可变的,因为它的状态不能改变。

val表示不可变,var表示可变

解释一下,“val表示值,var表示变量”。

这在计算中是非常重要的区别(因为这两个概念定义了编程的本质),而OO几乎完全模糊了这一点,因为在OO中,唯一的公理是“所有东西都是对象”。因此,现在很多程序员倾向于不理解/欣赏/认可,因为他们被洗脑了,只“以面向对象的方式思考”。通常会导致像到处都是这样使用可变/可变对象,而值/不可变对象可能/通常会更好。

正如许多人所说,赋值给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。当你在其中enqueuedequeue对象时,你会得到一个新的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.Queuevar !例如:

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也有自己的优势。我们会看到未来会发生什么。

Val表示不可变,var表示可变

你可以把val想象成java编程语言的final键域或c++语言的const键域。

简单来说:

var = __abc0able

瓦尔 = __abc1variable + fin艾尔

从c++的角度思考,

val x: T

是否类似于指向非常量数据的常量指针

T* const x;

var x: T

类似于指向非常量数据的非常量指针吗

T* x;

val优于var增加了代码库的不可变性,这有助于其正确性、并发性和可理解性。

要理解常量指针指向非常量数据的含义,请考虑以下Scala代码片段:

val m = scala.collection.mutable.Map(1 -> "picard")
m // res0: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard)

这里是指针 val m是常量,所以我们不能像这样将它重新赋值为指向其他对象

m = n // error: reassignment to val

然而,我们确实可以像这样改变m指向的非常量数据本身

m.put(2, "worf")
m // res1: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard, 2 -> worf)

就像它的名字一样简单。

Var表示它可以变化

Val表示不变

Val值是类型存储常量。一旦创建它的价值不能重新分配。可以用关键字val定义一个新值。

如。val x: Int = 5

这里type是可选的,因为scala可以从赋值推断它。

变量是类型化的存储单元,只要保留内存空间,就可以再次为其赋值。

如。var x: Int = 5

一旦不再需要存储在这两个存储单元中的数据,JVM就会自动取消对它们的分配。

在scala中,值比变量更受欢迎,因为它们给代码带来了稳定性,尤其是在并发和多线程代码中。

虽然很多人已经回答了瓦尔var之间的区别。 但是需要注意的一点是瓦尔和final不太一样关键字

我们可以用递归改变val的值,但我们永远不能改变final的值。Final比Val更恒定。

def factorial(num: Int): Int = {
if(num == 0) 1
else factorial(num - 1) * num
}

方法参数默认为val,并且在每次调用时都会更改值。

Val表示它的最后,不能是重新分配

然而,Var可以是重新分配后

在javascript方面,它与

< p > val→常量 var→var < / p >