Dan*_*ral 322
正如许多其他人所说,分配给a的对象val无法替换,并且对象分配给varcan.但是,所述对象可以修改其内部状态.例如:
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.
}
}
Run Code Online (Sandbox Code Playgroud)
因此,即使我们无法更改分配给的对象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")
Run Code Online (Sandbox Code Playgroud)
对于多线程系统,这一点尤为重要.在多线程系统中,可能发生以下情况:
x = new B(1)
f(x)
if (x.value.value == 1) {
print(x.value.value) // Can be different than 1!
}
Run Code Online (Sandbox Code Playgroud)
如果你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
}
Run Code Online (Sandbox Code Playgroud)
此代码快速且易于理解.它的主要缺点是传递的队列被修改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)
}
Run Code Online (Sandbox Code Playgroud)
因为我无法重用某些变量来跟踪我num,就像在前面的例子中一样,我需要求助于递归.在这种情况下,它是一个尾递归,具有相当不错的性能.但情况并非总是如此:有时候没有好的(可读的,简单的)尾递归解决方案.
但请注意,我可以重写该代码以同时使用a immutable.Queue和a 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
}
Run Code Online (Sandbox Code Playgroud)
此代码仍然有效,不需要递归,并且您无需担心在调用之前是否必须复制队列toNum.当然,我避免将变量重用于其他目的,并且此函数之外的代码都没有看到它们,因此我不需要担心它们的值从一行变为下一行 - 除非我明确这样做.
如果程序员认为程序员认为它是最好的解决方案,Scala选择让程序员这样做.其他语言选择使这些代码变得困难.Scala(以及任何具有广泛可变性的语言)付出的代价是编译器在优化代码方面没有那么多的余地.Java的答案是根据运行时配置文件优化代码.我们可以继续讨论各方的利弊.
就个人而言,我认为斯卡拉目前正在取得适当的平衡.到目前为止,它并不完美.我认为Clojure和Haskell都有非常有趣的概念,Scala没有采用,但Scala也有自己的优势.我们将看到未来会发生什么.
Jac*_*vis 57
val是最终的,也就是说,不能设置.想想final在java中.
oxb*_*kes 20
不同之处在于a var可以重新分配,而a 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.
Run Code Online (Sandbox Code Playgroud)
鉴于:
val n = immutable.Set("London", "Paris")
n = immutable.Set("New York") //Will not compile as n is a val.
Run Code Online (Sandbox Code Playgroud)
因此:
val n = mutable.Set("London", "Paris")
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable.
Run Code Online (Sandbox Code Playgroud)
如果您正在构建数据结构并且其所有字段都是vals,那么该数据结构因此是不可变的,因为其状态不能更改.
Mar*_*lic 13
从C++的角度思考,
val x: T
Run Code Online (Sandbox Code Playgroud)
类似于指向非常数数据的常量指针
T* const x;
Run Code Online (Sandbox Code Playgroud)
而
var x: T
Run Code Online (Sandbox Code Playgroud)
类似于非常量数据的非常量指针
T* x;
Run Code Online (Sandbox Code Playgroud)
有利于val在var增加,可促进其正确性,并发性和可理解的代码库的不变性.
"val表示不可变,var表示可变."
换句话说,"val表示值,var表示变量".
在计算中恰好非常重要的区别(因为这两个概念定义了编程的本质),并且OO几乎完全模糊了,因为在OO中,唯一的公理是"一切都是宾语".因此,如今许多程序员往往不理解/欣赏/认识,因为他们已被洗脑成"专注于OO方式".当值/不可变对象可能/通常会更好时,通常会导致变量/可变对象像所有地方一样被使用.
| 归档时间: |
|
| 查看次数: |
105026 次 |
| 最近记录: |