Answers:
这个问题很常见。困难的是找到重复项。
您应该争取参照透明性。这意味着,如果我有一个表达式“ e”,则可以制作一个val x = e
,并替换e
为x
。这是可变性中断的属性。每当您需要做出设计决策时,请最大化参考透明性。
实际上,局部方法var
是var
存在的最安全的方法,因为它不会逃脱方法。如果方法很短,那就更好了。如果不是,请尝试通过提取其他方法来减少它。
另一方面,可变集合有逃避的可能性,即使没有。更改代码时,您可能想要将其传递给其他方法或将其返回。这种事情破坏了参照透明性。
在一个对象(一个字段)上,几乎发生了相同的事情,但是后果更加严重。无论哪种方式,对象都将具有状态,并因此破坏参照透明性。但是拥有可变的集合意味着即使对象本身也可能失去对更改对象的控制。
immutable val
过immutable var
过mutable val
过mutable var
。特别是immutable var
结束mutable val
!
var
。使用不可变集合的另一个不错的功能是,即使var
变异,您也可以有效地保留旧副本。
var x: Set[Int]
, val x: mutable.Set[Int]
因为如果您传递x
给其他函数,则在前一种情况下,您可以确定该函数不会x
为您突变。
如果您使用不可变集合,并且需要“修改”它们,例如,将元素添加到循环中,则必须使用var
s,因为您需要将结果集合存储在某个地方。如果仅从不可变集合中读取,则使用val
。
通常,请确保不要混淆引用和对象。val
s是不可变的引用(C中的常量指针)。也就是说,当你使用val x = new MutableFoo()
,你就可以更改的对象是x
点,但你将无法切换到一个目标 x
点。如果使用,则相反var x = new ImmutableFoo()
。提起我的初步建议:如果不需要更改参考指向的对象,请使用val
。
var immutable = something(); immutable = immutable.update(x)
达不到使用不可变集合的目的。您已经放弃了参照透明性,通常可以从可变集合中获得相同的效果,并且时间复杂度更高。在这四种可能性(val
and var
,可变和不变)中,这最没有意义。我经常使用val mutable
。
var list: List[X] = Nil; list = item :: list; ...
而我忘记了曾经写过不同的文章。
回答这个问题的最好方法是举一个例子。假设由于某种原因,我们有一些仅收集数字的过程。我们希望记录这些数字,并将集合发送到另一个进程来执行此操作。
当然,在将集合发送到记录器之后,我们仍在收集数字。假设日志记录过程中有一些开销会延迟实际的日志记录。希望您能看到进展。
如果我们将此集合存储在mutable中val
,(因为我们不断对其进行添加,所以是可变的),这意味着进行日志记录的过程将查看仍由我们的集合过程更新的同一对象。该集合可能会随时更新,因此,当需要记录日志时,我们可能实际上未在记录发送的集合。
如果我们使用不可变var
,则将不可变数据结构发送到记录器。当我们向集合添加更多号码,我们将取代我们var
有一个新的不可变的数据结构。这并不意味着发送到记录器的集合将被替换!它仍然引用发送的集合。因此,我们的记录器确实会记录接收到的集合。
我认为本博文中的示例将更加清晰,因为在并发场景中使用哪种组合的问题变得更加重要:不变性对并发的重要性。同时,请注意同步与@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
不允许进行此重新分配。
mutable val
无法使用immutable var
。这里有什么不对吗?
+=
像数组缓冲区那样的方法。您的答案暗示那+=
是相同的,x = x + y
但事实并非如此。您将函数params视为vals的声明是正确的,并且确实收到所提到的错误,但这仅是因为您使用过=
。使用ArrayBuffer可能会遇到相同的错误,因此这里的集合可变性并不重要。因此,这不是一个很好的答案,因为它没有理解OP所谈论的内容。尽管这是一个很好的例子,说明如果您不打算传递可变的集合的危险。
ArrayBuffer
,通过使用Vector
。OP的问题很广泛,但是他们正在寻找有关何时使用的建议,因此我认为我的回答很有用,因为它说明了绕过可变集合的危险(这一事实val
无济于事)。immutable var
比更加安全mutable val
。