Scala 中 var 和 val 的區(qū)別

一想到這兩個(gè)的區(qū)別拘荡,大多數(shù)人第一反應(yīng)就是臼节,var 修飾的變量可改變,val 修飾的變量不可改變珊皿;但真的如此嗎网缝?事實(shí)上,var 修飾的對(duì)象引用可以改變亮隙,val 修飾的則不可改變,但對(duì)象的狀態(tài)卻是可以改變的垢夹。例如:

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) // 錯(cuò)誤溢吻,因?yàn)?x 為 val 修飾的,引用不可改變
    x.value = new A(6) // 錯(cuò)誤,因?yàn)?x.value 為 val 修飾的促王,引用不可改變
    x.value.value = 6 // 正確犀盟,x.value.value 為var 修飾的,可以重新賦值
  }
}

對(duì)于變量的不變性有很許多的好處蝇狼。

一是阅畴,如果一個(gè)對(duì)象不想改變其內(nèi)部的狀態(tài),那么由于不變性迅耘,我們不用擔(dān)心程序的其他部分會(huì)改變對(duì)象的狀態(tài)贱枣;例如

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

這對(duì)于多線程(多進(jìn)程)的系統(tǒng)來(lái)說(shuō)尤其重要。在一個(gè)多線程系統(tǒng)中颤专,以下可能發(fā)生:

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

如果你只使用 val 纽哥,并且只使用不可變的數(shù)據(jù)結(jié)構(gòu)(即是,避免使用 arrays 栖秕,scala.collection.mutable 內(nèi)的任何東西春塌,等等),你可以放心簇捍,這是不會(huì)發(fā)生的只壳;除非有一些代碼,例如一個(gè)框架暑塑,進(jìn)行反射——反射可以改變不可變的值吼句。

二是,當(dāng)用 var 修飾的時(shí)候梯投,你可能在多個(gè)地方重用 var 修飾的變量命辖,這樣會(huì)產(chǎn)生下面的問(wèn)題:

  • 對(duì)于閱讀代碼的人來(lái)說(shuō),在代碼的確定部分中知道變量的值是比較困難的分蓖;
  • 你可能會(huì)在使用代碼前初始化代碼尔艇,這樣會(huì)導(dǎo)致錯(cuò)誤;

因此么鹤,簡(jiǎn)單地說(shuō)终娃,使用 val 是安全和增強(qiáng)代碼可讀性的。

既然 val 有這么多的好處蒸甜,那為什么還要使用(或者說(shuō)存在) var 棠耕; 的確,有些編程語(yǔ)言只存在 val的情況 柠新,但是有些情況下窍荧,使用可變性可以大幅度提高程序的執(zhí)行效率。

例如恨憎,對(duì)于一個(gè)不可變的 Queue 蕊退,當(dāng)每次對(duì)隊(duì)列進(jìn)行 enqueue 和 dequeue 操作時(shí)郊楣,將會(huì)得到一個(gè)新的 Queue 對(duì)象,那么 瓤荔,如何處理所有的項(xiàng)目呢净蚤?

下面我們通過(guò)例子進(jìn)行解釋。假設(shè)一個(gè) Int 類型的隊(duì)列输硝,對(duì)隊(duì)列內(nèi)的所有數(shù)字進(jìn)行組合今瀑;例如隊(duì)列的元素有 1,2点把,3橘荠,那么組合后的數(shù)字就是 123。下面是第一種解決方案:采用的是 mutable.Queue

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

上面的代碼易于閱讀和理解愉粤,但存在一個(gè)主要的問(wèn)題是砾医,會(huì)改變?cè)磾?shù)據(jù),因此在調(diào)用 toNum 方法前必須對(duì)源數(shù)據(jù)進(jìn)行拷貝衣厘,避免對(duì)源數(shù)據(jù)產(chǎn)生污染如蚜。這時(shí)一種對(duì)對(duì)象進(jìn)行不變性管理的方法。

接下來(lái)采用 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)
}

因?yàn)?num 不能被重新分配值影暴,就像在前面的例子中一樣错邦,因此需要使用遞歸。這是一個(gè)尾部遞歸型宙,它的性能很好撬呢。但情況并非總是如此:有時(shí)根本就沒有好的(可讀的、簡(jiǎn)單的)尾遞歸解決方案妆兑。

下面采用 immutable.Queue 和 mutable.Queue 對(duì)代碼進(jìn)行重寫:

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
}

這段代碼非常有效魂拦,不需要遞歸,也無(wú)需擔(dān)心是否需要在調(diào)用toNum之前復(fù)制隊(duì)列搁嗓。自然地芯勘,我避免了由于其他用途而對(duì)變量進(jìn)行重用,由于在這個(gè)函數(shù)之外沒有任何代碼可以看到(修改)它們腺逛,所以我不需要擔(dān)心它們的值在代碼的其他地方會(huì)發(fā)生——除非我明確地這么做荷愕。

如果程序員認(rèn)為某種解決方案是最好的解決方案,那么 Scala 就允許程序員這么做棍矛。其他變成語(yǔ)言則沒有這么大的靈活性安疗。Scala (以及任何具有廣泛可變性的語(yǔ)言)的代價(jià)是够委,編譯器在優(yōu)化代碼方面沒有足夠的靈活性荐类。Java的提供解決方案是基于運(yùn)行時(shí)來(lái)優(yōu)化代碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末茁帽,一起剝皮案震驚了整個(gè)濱河市玉罐,隨后出現(xiàn)的幾起案子真竖,更是在濱河造成了極大的恐慌,老刑警劉巖厌小,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異战秋,居然都是意外死亡璧亚,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門脂信,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)癣蟋,“玉大人,你說(shuō)我怎么就攤上這事狰闪》杞粒” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵埋泵,是天一觀的道長(zhǎng)幔欧。 經(jīng)常有香客問(wèn)我,道長(zhǎng)丽声,這世上最難降的妖魔是什么礁蔗? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮雁社,結(jié)果婚禮上浴井,老公的妹妹穿的比我還像新娘。我一直安慰自己霉撵,他們只是感情好磺浙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著徒坡,像睡著了一般撕氧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上崭参,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天呵曹,我揣著相機(jī)與錄音,去河邊找鬼何暮。 笑死奄喂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的海洼。 我是一名探鬼主播跨新,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼坏逢!你這毒婦竟也來(lái)了域帐?” 一聲冷哼從身側(cè)響起赘被,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肖揣,沒想到半個(gè)月后民假,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡龙优,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年羊异,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彤断。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡野舶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宰衙,到底是詐尸還是另有隱情平道,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布供炼,位于F島的核電站一屋,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏袋哼。R本人自食惡果不足惜陆淀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望先嬉。 院中可真熱鬧轧苫,春花似錦、人聲如沸疫蔓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)衅胀。三九已至岔乔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間滚躯,已是汗流浹背雏门。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留掸掏,地道東北人茁影。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像丧凤,于是被迫代替她去往敵國(guó)和親募闲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容