Scala 中的 val-mutable 与 var-immutable

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11386559/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 04:20:30  来源:igfitidea点击:

val-mutable versus var-immutable in Scala

scalacollectionsfunctional-programmingimmutability

提问by James McCabe

Are there any guidelines in Scala on when to use val with a mutable collection versus using var with an immutable collection? Or should you really aim for val with an immutable collection?

Scala 中是否有关于何时将 val 与可变集合一起使用与将 var 与不可变集合一起使用的指南?或者你真的应该以不可变集合为目标?

The fact that there are both types of collection gives me a lot of choice, and often I don't know how to make that choice.

两种类型的收藏都给了我很多选择,而我常常不知道如何做出选择。

回答by Daniel C. Sobral

Pretty common question, this one. The hard thing is finding the duplicates.

很常见的问题,这个。困难的是找到重复项。

You should strive for referential transparency. What that means is that, if I have an expression "e", I could make a val x = e, and replace ewith x. This is the property that mutability break. Whenever you need to make a design decision, maximize for referential transparency.

您应该争取参考透明度。这也就意味着,如果我有一个表达的“e”,我可以做一个val x = e,并更换ex。这是可变性破坏的属性。每当您需要做出设计决策时,请最大化参考透明度。

As a practical matter, a method-local varis the safest varthat exists, since it doesn't escape the method. If the method is short, even better. If it isn't, try to reduce it by extracting other methods.

实际上,局部方法var是最安全的var,因为它不会逃避方法。如果方法简短,那就更好了。如果不是,请尝试通过提取其他方法来减少它。

On the other hand, a mutable collection has the potentialto escape, even if it doesn't. When changing code, you might then want to pass it to other methods, or return it. That's the kind of thing that breaks referential transparency.

另一方面,可变集合有可能逃脱,即使它没有。更改代码时,您可能希望将其传递给其他方法,或将其返回。这就是破坏引用透明度的事情。

On an object (a field), pretty much the same thing happens, but with more dire consequences. Either way the object will have state and, therefore, break referential transparency. But having a mutable collection means even the object itself might lose control of who's changing it.

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

回答by Malte Schwerhoff

If you work with immutable collections and you need to "modify" them, for example, add elements to them in a loop, then you have to use vars because you need to store the resulting collection somewhere. If you only read from immutable collections, then use vals.

如果您使用不可变集合并且需要“修改”它们,例如,在循环中向它们添加元素,那么您必须使用vars,因为您需要将结果集合存储在某处。如果您只从不可变集合中读取,则使用vals。

In general, make sure that you don't confuse references and objects. vals are immutable references (constant pointers in C). That is, when you use val x = new MutableFoo(), you'll be able to change the objectthat xpoints to, but you won't be able to change to which objectxpoints. The opposite holds if you use var x = new ImmutableFoo(). Picking up my initial advice: if you don't need to change to which object a reference points, use vals.

通常,请确保不要混淆引用和对象。vals 是不可变的引用(C 中的常量指针)。也就是说,当你使用val x = new MutableFoo(),你就可以更改的对象x点,但你将无法切换到一个目标x点。如果您使用var x = new ImmutableFoo(). 接受我最初的建议:如果您不需要更改引用指向的对象,请使用vals。

回答by jmazin

The best way to answer this is with an example. Suppose we have some process simply collecting numbers for some reason. We wish to log these numbers, and will send the collection to anotherprocess to do this.

回答这个问题的最好方法是举个例子。假设我们有一些过程出于某种原因只是收集数字。我们希望记录这些数字,并将集合发送到另一个进程来执行此操作。

Of course, we are still collecting numbers after we send the collection to the logger. And let's say there is some overhead in the logging process that delays the actual logging. Hopefully you can see where this is going.

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

If we store this collection in a mutable val, (mutable because we are continuously adding to it), this means that the process doing the logging will be looking at the same objectthat's still being updated by our collection process. That collection may be updated at any time, and so when it's time to log we may not actually be logging the collection we sent.

如果我们将此集合存储在一个 mutableval中(可变,因为我们不断向它添加内容),这意味着执行日志记录的进程将查看仍在由我们的集合进程更新的同一个对象。该集合可能随时更新,因此当需要记录时,我们实际上可能不会记录我们发送的集合。

If we use an immutable var, we send an immutable data structure to the logger. When we add more numbers to our collection, we will be replacingour varwith a new immutable data structure. This doesn't mean collection sent to the logger is replaced! It's still referencing the collection it was sent. So our logger will indeed log the collection it received.

如果我们使用 immutable var,我们会向记录器发送一个不可变的数据结构。当我们向集合添加更多号码,我们将取代我们var有一个新的不可变的数据结构。这并不意味着发送到记录器的集合会被替换!它仍然引用它发送的集合。所以我们的记录器确实会记录它收到的集合。

回答by Bogdan Nicolau

I think the examples in this blog post will shed more light, as the question of which combo to use becomes even more important in concurrency scenarios: importance of immutability for concurrency. And while we're at it, note the preferred use of synchronised vs @volatile vs something like AtomicReference: three tools

我认为这篇博文中的示例将更加清晰,因为在并发场景中使用哪种组合的问题变得更加重要:并发的不变性的重要性。当我们在做的时候,注意synchronized vs @volatile vs 像AtomicReference这样的东西的首选使用:三个工具

回答by Akavall

var immutablevs. val mutable

var immutable对比 val mutable

In addition to many excellent answers to this question. Here is a simple example, that illustrates potential dangers of val mutable:

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

Mutable objects can be modified inside methods, that take them as parameters, while reassignment is not allowed.

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

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}")
    }
}

Result:

结果:

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

Something like this cannot happen with var immutable, because reassignment is not allowed:

不会发生这样的事情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}")
    }
}

Results in:

结果是:

error: reassignment to val

Since function parameters are treated as valthis reassignment is not allowed.

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