在Scala中val-mutable与var-immutable

在Scala中有没有关于什么时候对可变集合使用val与将不可变集合使用var的指导? 或者你应该真正的目标为val与一个不可改变的集合?

collections这两种types的事实给了我很多的select,而且我经常不知道如何做出这样的select。

很常见的问题,这一个。 困难的是find重复的东西。

你应该争取参考透明度 。 这意味着,如果我有一个expression式“e”,我可以做一个val x = e ,并用xreplacee 。 这是可变性破坏的属性。 无论何时您需要做出devise决策,最大限度地提高参考透明度。

实际上,方法局部var是最安全的var ,因为它不能逃避方法。 如果方法很短,甚至更好。 如果不是,则尝试通过提取其他方法来减less它。

另一方面,一个可变的collections有可能逃脱,即使没有。 当改变代码时,你可能想把它传递给其他方法,或者返回它。 这是违背参照透明度的事情。

在一个对象(一个字段)上,几乎是一样的事情发生,但有更可怕的后果。 无论哪种方式对象将有状态,因此,破坏参考透明度。 但有一个可变的collections意味着即使对象本身可能会失去控制谁在改变它。

如果使用不可变集合,并且需要“修改”它们,例如,在循环中向它们添加元素,则必须使用var s,因为需要将结果集合存储在某处。 如果你只读取不可变的集合,然后使用val s。

一般来说,请确保不要混淆引用和对象。 val s是不可变的引用(C中的常量指针)。 也就是说,当你使用val x = new MutableFoo() ,你将能够改变 x指向的对象 ,但是你将不能改变到哪个对象的 x点。 如果使用var x = new ImmutableFoo()则相反。 捡起我最初的build议:如果你不需要改变参考点的对象,使用val s。

回答这个问题的最好方法就是一个例子。 假设我们有一些过程只是简单地收集数字出于某种原因。 我们希望logging这些数字,并将收集到另一个进程来做到这一点。

当然,我们在收集到logging器后仍然在收集数字。 假设在日志logging过程中有一些开销会延迟实际的日志logging。 希望你能看到这是怎么回事。

如果我们将这个集合存储在一个可变的val中(可变的,因为我们不断地添加到它),这意味着进行日志logging的过程将看着我们收集过程中仍在更新的同一个对象 。 这个集合可能随时更新,所以当logging时间的时候,我们实际上可能不会logging我们发送的集合。

如果我们使用一个不可变的var ,我们发送一个不可变的数据结构给logging器。 当我们为我们的集合添加更多数字时,我们将 新的不可变数据结构replace我们的var 。 这并不意味着收集发送到logging器被取代! 它仍然参考它发送的集合。 所以我们的logging器确实会logging它收到的收集。

我认为这个博客文章中的例子将会更加清晰,因为在并发场景中使用哪个组合的问题变得更加重要:并发性的不可变性的重要性 。 而当我们在这时,请注意更喜欢使用同步vs @ volatile与像AtomicReference: 三个工具

var immutableval mutable

除了这个问题的许多优秀的答案。 这是一个简单的例子,说明了val mutable潜在危险:

可变对象可以在方法内部修改,将其作为参数,而不允许重新分配。

 import scala.collection.mutable.ArrayBuffer object MyObject { def main(args: Array[String]) { val a = ArrayBuffer(1,2,3,4) silly(a) println(a) // a has been modified here } def silly(a: ArrayBuffer[Int]): Unit = { a += 10 println(s"length: ${a.length}") } } 

结果:

 length: 5 ArrayBuffer(1, 2, 3, 4, 10) 

像这样的东西不能发生与var immutable ,因为重新分配是不允许的:

 object MyObject { def main(args: Array[String]) { var v = Vector(1,2,3,4) silly(v) println(v) } def silly(v: Vector[Int]): Unit = { v = v :+ 10 // This line is not valid println(s"length of v: ${v.length}") } } 

结果是:

 error: reassignment to val 

由于函数参数被视为val因此不允许重新分配。