在Scala中,是否有关于何时使用可变集合的val与不可变集合的var的指导方针?或者您真的应该使用不可变集合的val?
事实上,存在两种类型的集合给了我很多选择,但通常我不知道如何做出选择。
在Scala中,是否有关于何时使用可变集合的val与不可变集合的var的指导方针?或者您真的应该使用不可变集合的val?
事实上,存在两种类型的集合给了我很多选择,但通常我不知道如何做出选择。
这是一个相当常见的问题。难点在于找出重复项。
你应该追求 引用透明度。这意味着,如果我有一个表达式“e”,我可以创建一个val x = e
,并用x
代替e
。这就是可变性破坏的属性。每当你需要做出设计决策时,请最大化引用透明度。
从实际角度来看,方法局部的var
是最安全的var
,因为它不会逃出方法。如果方法很短,那就更好了。如果不是,尝试通过提取其他方法来减少方法长度。
另一方面,可变集合有可能会逃逸,即使它并没有逃逸。当更改代码时,您可能想将其传递给其他方法或返回它。这就是破坏引用透明度的类型。
在对象(字段)上,发生的事情基本相同,但后果更加严重。无论哪种方式,对象都将具有状态,因此会破坏引用透明度。但有了可变集合,甚至对象本身也可能失去控制权。
immutable val
,而不是immutable var
,更喜欢使用mutable val
,而不是mutable var
,特别是比mutable val
更喜欢使用immutable var
! - Peter Schmitzvar
。使用不可变集合的另一个好处是,即使var
发生变化,您也可以有效地保留旧副本。 - Mysterious Danvar x: Set[Int]
,而不是val x: mutable.Set[Int]
,因为在前者的情况下,如果将x
传递给其他函数,则可以确保该函数无法更改x
。 - pathikritvar
,因为您需要在某个地方存储结果集合。如果您仅从不可变集合中读取,则请使用val
。val
是不可变的引用(C语言中的常量指针)。也就是说,当您使用val x = new MutableFoo()
时,您可以更改x
指向的对象,但您将无法更改x
指向的对象。如果您不需要更改引用所指向的对象,请使用val
。var immutable = something(); immutable = immutable.update(x)
这样做违背了使用不可变集合的初衷。您已经放弃了引用透明性,并且通常可以从具有更好时间复杂度的可变集合中获得相同的效果。在四种可能性(val
和 var
,可变和不可变)中,这种方法是最没有意义的。我经常使用 val mutable
。 - Jim Pivarskival
中(可变是因为我们正在不断添加它),这意味着执行记录的进程将查看仍在被我们的收集过程更新的相同对象。该收集可能随时更新,因此在记录时,我们可能实际上没有记录发送的收集。var
,我们向记录器发送不可变数据结构。当我们向我们的收集添加更多数字时,我们将使用新的不可变数据结构替换我们的var
。这并不意味着发送到记录器的收集被替换了!它仍然引用它被发送的集合。因此,我们的记录器确实会记录它收到的收集。var immutable
vs. 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
实现的。这里有什么错误吗? - Akavall+=
方法,就像阵列缓冲区一样。你的答案暗示着 +=
和 x = x + y
是相同的,但实际并不是这样。你关于函数参数被视为 vals 的说法是正确的,并且你确实会得到你提到的错误,但这仅仅是因为你使用了 =
。你可以通过 ArrayBuffer 来获得同样的错误,所以这里集合的可变性并不是很相关。因此,这不是一个很好的答案,因为它没有涉及到 OP 所讨论的问题。虽然这是一个很好的例子,说明传递一个可变集合的危险性,如果你没有打算这样做的话。 - EdgeCaseBerg