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

时间:2009-11-24 16:57:39

标签: scala

Scala中的varval定义之间有什么区别?为什么语言需要两者?为什么要选择val而不是var,反之亦然?

12 个答案:

答案 0 :(得分:313)

正如许多其他人所说,分配给val的对象无法替换,分配给var的对象可以。但是,所述对象可以修改其内部状态。例如:

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.
  }
}

因此,即使我们无法更改分配给x的对象,我们也可以更改该对象的状态。然而,它的根源是var

现在,由于许多原因,不变性是一件好事。首先,如果一个对象没有改变内部状态,你不必担心代码的其他部分是否正在改变它。例如:

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

对于多线程系统,这一点变得尤为重要。在多线程系统中,可能会发生以下情况:

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

如果你只使用val,并且只使用不可变数据结构(即避免数组,scala.collection.mutable中的所有内容等),你可以放心,这不会发生。也就是说,除非有一些代码,甚至是一个框架,做反射技巧 - 不幸的是,反射可以改变“不可变”值。

这是一个原因,但还有另一个原因。当您使用var时,您可能会尝试重复使用相同的var用于多种用途。这有一些问题:

  • 阅读代码的人更难以知道代码的某个部分中变量的值是什么。
  • 您可能忘记在某些代码路径中重新初始化变量,并最终在代码中向下游传递错误的值。

简单地说,使用val更安全,可以使代码更易读。

然后,我们可以走向另一个方向。如果val更好,为什么要var呢?好吧,有些语言确实采用了这种方式,但在某些情况下,可变性可以提高性能。

例如,采用不可变Queue。如果您有enqueuedequeue项内容,则会获得一个新的Queue对象。那么,你会如何处理其中的所有项目?

我将通过一个例子来说明这一点。假设您有一个数字队列,并且您想要从中编写一个数字。例如,如果我有一个顺序为2,1,3的队列,我想要取回数字213.让我们先用mutable.Queue解决它:

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

此代码快速且易于理解。它的主要缺点是传递的队列由toNum修改,因此您必须事先复制它。这就是不变性让你摆脱的对象管理。

现在,我们将其转换为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)
}

因为我不能重用某个变量来跟踪我的num,就像前面的例子一样,我需要求助于递归。在这种情况下,它是一个尾递归,具有相当不错的性能。但情况并非总是如此:有时候没有好的(可读的,简单的)尾递归解决方案。

但请注意,我可以重写该代码,同时使用immutable.Queuevar!例如:

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
}

此代码仍然有效,不需要递归,您无需担心在调用toNum之前是否必须复制队列。当然,我避免将变量重用于其他目的,并且此函数之外的代码都没有看到它们,所以我不需要担心它们的值从一行变为下一行 - 除非我明确这样做。

如果程序员认为程序员认为它是最好的解决方案,那么Scala选择让程序员这样做。其他语言选择使这些代码变得困难。 Scala(以及任何具有广泛可变性的语言)付出的代价是编译器在优化代码方面没有那么多的余地。 Java的答案是根据运行时配置文件优化代码。我们可以继续谈谈各方的利弊。

就个人而言,我认为斯卡拉目前正在取得适当的平衡。到目前为止,它并不完美。我认为ClojureHaskell都有非常有趣的概念,Scala没有采用这些概念,但Scala也有自己的优势。我们将看到未来会发生什么。

答案 1 :(得分:57)

val是最终的,也就是说,无法设置。在java中思考final

答案 2 :(得分:45)

简单来说:

var = var iable

val = v ariable + fin al

答案 3 :(得分:20)

不同之处在于var可以重新分配,而val则不能。任何实际分配的可变性或其他方面都是一个副问题:

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.

鉴于:

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

因此:

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

如果您正在构建数据结构并且其所有字段都是val,那么该数据结构因此是不可变的,因为其状态不能更改。

答案 4 :(得分:19)

val表示不可变,var表示可变。

Full discussion.

答案 5 :(得分:12)

用C ++思考,

val x: T

类似于指向非常数数据的常量指针

T* const x;

var x: T 

类似于非常量数据的非常量指针

T* x;

支持val超过var会增加代码库的不变性,从而促进其正确性,并发性和可理解性。

答案 6 :(得分:8)

“val表示不可变,var表示可变。”

换句话说,“val表示值,var表示变量”。

这种区别恰好在计算中非常重要(因为这两个概念定义了编程的本质),并且OO几乎完全模糊了,因为在OO中,唯一的公理是“一切都是一个对象“。因此,如今许多程序员往往不理解/欣赏/认识,因为他们已被洗脑成“专注于OO方式”。通常导致变量/可变对象被用作无处不在,当值/不可变对象可能/通常会更好。

答案 7 :(得分:6)

  

val表示不可变,var表示可变

您可以将val视为java编程语言final密钥世界或c ++语言const密钥世界。

答案 8 :(得分:1)

它就像它的名字一样简单。

  

var意味着它可以变化

     

val意味着不变

答案 9 :(得分:0)

Val - 值是类型化存储常量。一旦创建,其值不能重新分配。可以使用关键字val定义新值。

例如。 val x:Int = 5

此类型是可选的,因为scala可以从指定的值推断出它。

Var - 变量是类型化存储单元,只要保留存储空间,就可以再次为其分配值。

例如。 var x:Int = 5

存储在两个存储单元中的数据在不再需要时由JVM自动解除分配。

在scala中,值优先于变量,因为它们会给代码带来稳定性,特别是在并发和多线程代码中。

答案 10 :(得分:0)

尽管许多人已经回答了 Val var 之间的区别。 但是需要注意的一点是, val与final 关键字并不完全一样。

我们可以使用递归来更改val的值,但永远不能更改final的值。 Final比Val更稳定。

def factorial(num: Int): Int = {
 if(num == 0) 1
 else factorial(num - 1) * num
}

默认情况下,方法参数为val,并且每次调用值都被更改。

答案 11 :(得分:0)

get '/login', to: 'sessions#new' post '/login', to: 'sessions#create' delete '/logout', to: 'sessions#destroy' 表示其最终,不能重新分配

Val可以稍后重新分配

相关问题