在Scala中val-mutable与var-immutable
在Scala中有没有关于什么时候对可变集合使用val与将不可变集合使用var的指导? 或者你应该真正的目标为val与一个不可改变的集合?
collections这两种types的事实给了我很多的select,而且我经常不知道如何做出这样的select。
很常见的问题,这一个。 困难的是find重复的东西。
你应该争取参考透明度 。 这意味着,如果我有一个expression式“e”,我可以做一个val x = e
,并用x
replacee
。 这是可变性破坏的属性。 无论何时您需要做出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 immutable
与val 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
因此不允许重新分配。