Scala中var和val定义有什么区别?

本文翻译自:What is the difference between a var and val definition in Scala?

What is the difference between a var and val definition in Scala and why does the language need both? Scala中varval定义之间有什么区别?为什么语言需要两者? Why would you choose a val over a var and vice versa? 为什么你会选择val而不是var ,反之亦然?


#1楼

参考:https://stackoom.com/question/7w1G/Scala中var和val定义有什么区别


#2楼

val means immutable and var means mutable. val表示不可变, var表示可变。

Full discussion. 全面讨论。


#3楼

val is final, that is, cannot be set. val是final,也就是说,无法设置。 Think final in java. 在java中思考final


#4楼

The difference is that a var can be re-assigned to whereas a val cannot. 不同之处在于var可以重新分配,而val则不能。 The mutability, or otherwise of whatever is actually assigned, is a side issue: 任何实际分配的可变性或其他方面都是一个副作用:

import collection.immutable
import collection.mutable
var m = immutable.Set("London", "Paris")
m = immutable.Set("New York") //Reassignment - I have change the "value" at m.

Whereas: 鉴于:

val n = immutable.Set("London", "Paris")
n = immutable.Set("New York") //Will not compile as n is a val.

And hence: 因此:

val n = mutable.Set("London", "Paris")
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable.

If you are building a data structure and all of its fields are val s, then that data structure is therefore immutable, as its state cannot change. 如果要构建数据结构并且其所有字段都是val ,那么该数据结构因此是不可变的,因为其状态不能更改。


#5楼

"val means immutable and var means mutable." “val表示不可变,var表示可变。”

To paraphrase, "val means value and var means variable". 换句话说,“val表示值,var表示变量”。

A distinction that happens to be extremely important in computing (because those two concepts define the very essence of what programming is all about), and that OO has managed to blur almost completely, because in OO, the only axiom is that "everything is an object". 在计算中恰好非常重要的区别(因为这两个概念定义了编程的本质),并且OO几乎完全模糊了,因为在OO中,唯一的公理是“一切都是宾语”。 And that as a consequence, lots of programmers these days tend not to understand/appreciate/recognize, because they have been brainwashed into "thinking the OO way" exclusively. 因此,如今许多程序员往往不理解/欣赏/认识,因为他们已被洗脑成“专注于OO方式”。 Often leading to variable/mutable objects being used like everywhere , when value/immutable objects might/would often have been better. 当值/不可变对象可能/通常会更好时,通常会导致变量/可变对象像所有地方一样被使用。


#6楼

As so many others have said, the object assigned to a val cannot be replaced, and the object assigned to a var can. 正如许多其他人所说,分配给val的对象不能被替换,并且分配给var的对象可以。 However, said object can have its internal state modified. 但是,所述对象可以修改其内部状态。 For example: 例如:

class A(n: Int) {
  var value = n
}

class B(n: Int) {
  val value = new A(n)
}

object Test {
  def main(args: Array[String]) {
    val x = new B(5)
    x = new B(6) // Doesn't work, because I can't replace the object created on the line above with this new one.
    x.value = new A(6) // Doesn't work, because I can't replace the object assigned to B.value for a new one.
    x.value.value = 6 // Works, because A.value can receive a new object.
  }
}

So, even though we can't change the object assigned to x , we could change the state of that object. 因此,即使我们无法更改分配给x的对象,我们也可以更改该对象的状态。 At the root of it, however, there was a var . 然而,在它的根源,有一个var

Now, immutability is a good thing for many reasons. 现在,由于许多原因,不变性是一件好事。 First, if an object doesn't change internal state, you don't have to worry if some other part of your code is changing it. 首先,如果一个对象没有改变内部状态,你不必担心代码的其他部分是否正在改变它。 For example: 例如:

x = new B(0)
f(x)
if (x.value.value == 0)
  println("f didn't do anything to x")
else
  println("f did something to x")

This becomes particularly important with multithreaded systems. 对于多线程系统,这一点尤为重要。 In a multithreaded system, the following can happen: 在多线程系统中,可能发生以下情况:

x = new B(1)
f(x)
if (x.value.value == 1) {
  print(x.value.value) // Can be different than 1!
}

If you use val exclusively, and only use immutable data structures (that is, avoid arrays, everything in scala.collection.mutable , etc.), you can rest assured this won't happen. 如果你只使用val ,并且只使用不可变数据结构(即避免数组, scala.collection.mutable所有内容等),你可以放心,这不会发生。 That is, unless there's some code, perhaps even a framework, doing reflection tricks -- reflection can change "immutable" values, unfortunately. 也就是说,除非有一些代码,甚至是一个框架,做反射技巧 - 不幸的是,反射可以改变“不可变”值。

That's one reason, but there is another reason for it. 这是一个原因,但还有另一个原因。 When you use var , you can be tempted into reusing the same var for multiple purposes. 当您使用var ,您可能会尝试重复使用相同的var用于多种目的。 This has some problems: 这有一些问题:

  • It will be more difficult for people reading the code to know what is the value of a variable in a certain part of the code. 阅读代码的人更难以知道代码的某个部分中变量的值是什么。
  • You may forget to re-initialize the variable in some code path, and end up passing wrong values downstream in the code. 您可能忘记在某些代码路径中重新初始化变量,并最终在代码中向下游传递错误的值。

Simply put, using val is safer and leads to more readable code. 简单地说,使用val更安全并且导致更易读的代码。

We can, then, go the other direction. 那么,我们可以走向另一个方向。 If val is that better, why have var at all? 如果val更好,为什么要有var Well, some languages did take that route, but there are situations in which mutability improves performance, a lot. 好吧,有些语言确实采用了这种方式,但在某些情况下,可变性可以提高性能。

For example, take an immutable Queue . 例如,采用不可变的Queue When you either enqueue or dequeue things in it, you get a new Queue object. 当您将其中的内容enqueuedequeue列时,您将获得一个新的Queue对象。 How then, would you go about processing all items in it? 那你怎么去处理它里面的所有物品呢?

I'll go through that with an example. 我将以一个例子来说明这一点。 Let's say you have a queue of digits, and you want to compose a number out of them. 假设您有一个数字队列,并且您想要从中编写一个数字。 For example, if I have a queue with 2, 1, 3, in that order, I want to get back the number 213. Let's first solve it with a mutable.Queue : 例如,如果我有一个mutable.Queue的队列,那么我想要取回213号。让我们先用mutable.Queue解决它:

def toNum(q: scala.collection.mutable.Queue[Int]) = {
  var num = 0
  while (!q.isEmpty) {
    num *= 10
    num += q.dequeue
  }
  num
}

This code is fast and easy to understand. 此代码快速且易于理解。 Its main drawback is that the queue that is passed is modified by toNum , so you have to make a copy of it beforehand. 它的主要缺点是传递的队列由toNum修改,因此您必须事先复制它。 That's the kind of object management that immutability makes you free from. 这就是不变性让你摆脱的对象管理。

Now, let's covert it to an immutable.Queue : 现在,让我们把它转换成一个immutable.Queue

def toNum(q: scala.collection.immutable.Queue[Int]) = {
  def recurse(qr: scala.collection.immutable.Queue[Int], num: Int): Int = {
    if (qr.isEmpty)
      num
    else {
      val (digit, newQ) = qr.dequeue
      recurse(newQ, num * 10 + digit)
    }
  }
  recurse(q, 0)
}

Because I can't reuse some variable to keep track of my num , like in the previous example, I need to resort to recursion. 因为我不能重用一些变量来跟踪我的num ,就像前面的例子一样,我需要求助于递归。 In this case, it is a tail-recursion, which has pretty good performance. 在这种情况下,它是一个尾递归,具有相当不错的性能。 But that is not always the case: sometimes there is just no good (readable, simple) tail recursion solution. 但情况并非总是如此:有时候没有好的(可读的,简单的)尾递归解决方案。

Note, however, that I can rewrite that code to use an immutable.Queue and a var at the same time! 但请注意,我可以重写该代码以同时使用immutable.Queuevar For example: 例如:

def toNum(q: scala.collection.immutable.Queue[Int]) = {
  var qr = q
  var num = 0
  while (!qr.isEmpty) {
    val (digit, newQ) = qr.dequeue
    num *= 10
    num += digit
    qr = newQ
  }
  num
}

This code is still efficient, does not require recursion, and you don't need to worry whether you have to make a copy of your queue or not before calling toNum . 此代码仍然有效,不需要递归,您无需担心在调用toNum之前是否必须复制队列。 Naturally, I avoided reusing variables for other purposes, and no code outside this function sees them, so I don't need to worry about their values changing from one line to the next -- except when I explicitly do so. 当然,我避免将变量重用于其他目的,并且此函数之外的代码都没有看到它们,因此我不需要担心它们的值从一行变为下一行 - 除非我明确这样做。

Scala opted to let the programmer do that, if the programmer deemed it to be the best solution. 如果程序员认为程序员认为它是最好的解决方案,Scala选择让程序员这样做。 Other languages have chosen to make such code difficult. 其他语言选择使这些代码变得困难。 The price Scala (and any language with widespread mutability) pays is that the compiler doesn't have as much leeway in optimizing the code as it could otherwise. Scala(以及任何具有广泛可变性的语言)付出的代价是编译器在优化代码方面没有那么多的余地。 Java's answer to that is optimizing the code based on the run-time profile. Java的答案是根据运行时配置文件优化代码。 We could go on and on about pros and cons to each side. 我们可以继续讨论各方的利弊。

Personally, I think Scala strikes the right balance, for now. 就个人而言,我认为斯卡拉目前正在取得适当的平衡。 It is not perfect, by far. 到目前为止,它并不完美。 I think both Clojure and Haskell have very interesting notions not adopted by Scala, but Scala has its own strengths as well. 我认为ClojureHaskell都有非常有趣的概念,Scala没有采用,但Scala也有自己的优势。 We'll see what comes up on the future. 我们将看到未来会发生什么。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值