Scala中的var和val定义有什么区别?
Scala中的var
和val
定义有什么不同?语言为什么需要这两者? 为什么你会select一个val
,反之亦然?
正如许多其他人所说,分配给val
的对象不能被replace,而分配给var
的对象可以被replace。 但是,所述对象可以修改其内部状态。 例如:
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")
multithreading系统对此变得尤为重要。 在multithreading系统中,可能会发生以下情况:
x = new B(1) f(x) if (x.value.value == 1) { print(x.value.value) // Can be different than 1! }
如果你只使用val
,并且只使用不可变的数据结构(即避免数组, scala.collection.mutable
所有内容等),那么你可以放心,这是不会发生的。 也就是说,除非有一些代码,甚至可能是一个框架,做reflection技巧 – 不幸的是reflection会改变“不可变的”值。
这是一个原因,但是还有另一个原因。 当你使用var
,你可以试图重复使用同一个var
来达到多种目的。 这有一些问题:
- 阅读代码的人会更难以知道代码某个部分的variables的价值。
- 您可能忘记在某些代码path中重新初始化variables,并最终在代码中传递错误的值。
简而言之,使用val
更安全,并导致更易读的代码。
那么我们可以走向另一个方向。 如果val
更好,为什么有var
? 那么,一些语言确实采取了这样的路线,但有些情况下,可变性提高了性能,很多。
例如,采取一个不可变的Queue
。 当你入enqueue
或dequeue
时,你会得到一个新的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 }
此代码速度快,易于理解。 它的主要缺点是传递的队列被toNum
修改,所以你必须事先做一个拷贝。 这是不变性使你从中解脱出来的那种对象pipe理。
现在,让我们把它变成一个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) }
因为我不能重用一些variables来跟踪我的num
,就像在前面的例子中,我需要诉诸recursion。 在这种情况下,它是一个尾recursion,性能相当不错。 但情况并非总是如此:有时候没有好的(可读的,简单的)尾recursion解决scheme。
但是,请注意,我可以重写该代码以同时使用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 }
此代码仍然有效,不需要recursion,在调用toNum
之前,您不必担心是否必须复制队列。 当然,我避免将variables用于其他目的,除此之外的任何代码都不会看到它们,所以我不必担心它们的值从一行变到另一行 – 除非我明确这样做。
如果程序员认为它是最好的解决scheme,Scalaselect让程序员这样做。 其他语言select使这样的代码困难。 价格Scala(以及任何具有普遍可变性的语言)都付出代价,即编译器在优化代码方面没有那么好。 Java的答案是根据运行时configuration文件优化代码。 我们可以继续谈论每一方的利弊。
就我个人而言,现在我认为Scala达到了平衡。 到目前为止,这并不完美。 我认为Clojure和Haskell都有一些Scala没有采用的非常有趣的概念,但是Scala也有自己的优势。 我们将看到未来会发生什么。
val
是final的,也就是说不能设置。 在Java中考虑final
。
简单来说:
var = variable
val = va riable + fin al
val
表示不可变, var
表示可变。
充分讨论。
不同之处在于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.
如果你正在构build一个数据结构并且它的所有字段都是val
,那么这个数据结构是不可变的,因为它的状态不能改变。
用C ++来思考,
val x: T
类似于指向非常量数据的常量指针
T* const x;
而
var x: T
类似于对非常数数据的非常量指针
T* x;
val
对val
支持增加了代码库的不变性,这可以促进它的正确性,并发性和可理解性。
“val意味着不可变,var意味着可变。”
换句话说,“val意味着价值和var意味着variables”。
这个区别在计算中是非常重要的(因为这两个概念定义了编程的本质),而且OO几乎完全模糊了,因为在OO中,唯一的公理是“一切都是目的”。 因此,现在很多程序员往往不理解/欣赏/认可,因为他们被洗脑成为“专心思考OO方式”。 通常会导致variables/可变对象像所有地方一样被使用,当值/不可变对象可能/往往会更好。
val表示不可变,var表示可变
你可以把val
看作是java编程语言final
关键世界或者c ++语言的const
关键世界。
val类似于Java中的最终variables。 一旦初始化, val就不能被重新分配。
相比之下, var类似于Java中的非finalvariables。 var可以在整个生命周期中重新分配。
这就像它的名字一样简单。
var意味着它可以变化
val意思是不变的
Val值是键入的存储常量。 一旦创build它的价值不能被重新分配。 一个新的值可以用关键字val定义。
例如。 val x:Int = 5
这里的types是可选的,因为scala可以从分配的值中推断出来。
Var – variables是types化的存储单元,只要内存空间被保留,可以重新赋值。
例如。 var x:Int = 5
存储在两个存储单元中的数据一旦被不再需要,就会被JVM自动解除分配。
在scala中,由于这些代码带来的稳定性,尤其是在并发和multithreading代码中,所以值优于variables。