本篇繼續(xù)上一篇對(duì)Scala的整體介紹,本篇進(jìn)一步解釋Scala的一些高級(jí)特性,當(dāng)你學(xué)完本篇后,就有足夠的知識(shí)編寫一些實(shí)用的Scala腳本應(yīng)用了。
第七步:使用類型參數(shù)化數(shù)組
在Scala中你可以使用new來實(shí)例化一個(gè)類。當(dāng)你創(chuàng)建一個(gè)對(duì)象的實(shí)例時(shí),你可以使用數(shù)值或類型參數(shù)。如果使用類型參數(shù),它的作用類似Java或.Net的Generic類型。所不同的是Scala使用方括號(hào)來指明數(shù)據(jù)類型參數(shù),而非尖括號(hào)。比如
1valgreetStrings=newArray[String](3)
2greetStrings(0)="Hello"
3greetStrings(1)=","
4greetStrings(2)="world!\n"
5for(i <-0to2)
6print(greetStrings(i))
可以看到Scala使用[]來為數(shù)組指明類型化參數(shù),本例使用String類型,數(shù)組使用()而非[]來指明數(shù)組的索引。其中的for表達(dá)式中使用到 0 to 2 ,這個(gè)表達(dá)式演示了Scala的一個(gè)基本規(guī)則,如果一個(gè)方法只有一個(gè)參數(shù),你可以不用括號(hào)和. 來調(diào)用這個(gè)方法。
因此這里的 0 to 2, 其實(shí)為(0).to(2) 調(diào)用的為整數(shù)類型的 to方法,to方法使用一個(gè)參數(shù)。Scala中所有的基本數(shù)據(jù)類型也是對(duì)象(和Java不同),因此0 可以有方法(實(shí)際上調(diào)用的是RichInt的方法),這種只有一個(gè)參數(shù)的方法可以使用操作符的寫法(不用.和括號(hào)),實(shí)際上Scala中表達(dá)式 1+2 ,最終解釋為(1).+(2) +也是Int的一個(gè)方法,和Java不同的是,Scala對(duì)方法的名稱沒有太多的限制,你可以使用符合作為方法的名稱。
這里也說明為什么Scala中使用()來訪問數(shù)組元素,在Scala中,數(shù)組和其它普遍的類定義一樣,沒有什么特別之處,當(dāng)你在某個(gè)值后面使用()時(shí),Scala將其翻譯成對(duì)應(yīng)對(duì)象的apply方法。因此本例中g(shù)reetStrings(1) 其實(shí)調(diào)用greetString.apply(1)方法。這種表達(dá)方法不僅僅只限于數(shù)組,對(duì)于任何對(duì)象,如果在其后面使用(),都將調(diào)用該對(duì)象的apply方法。同樣的如果對(duì)某個(gè)使用()的對(duì)象賦值,比如:
1greetStrings(0)="Hello"
Scala將這種賦值轉(zhuǎn)換為該對(duì)象的update 方法, 也就是 greetStrings.update(0,”hello”). 因此上面的例子,使用傳統(tǒng)的方法調(diào)用可以寫成:
1valgreetStrings=newArray[String](3)
2greetStrings.update(0,"Hello")
3greetStrings.update(1,",")
4greetStrings.update(2,"world!\n")
5for(i <-0to2)
6print(greetStrings.apply(i))
從這點(diǎn)來說,數(shù)組在Scala中并不某種特殊的數(shù)據(jù)類型,和普通的類沒有什么不同。
不過Scala還是提供了初始化數(shù)組的簡(jiǎn)單的方法,比如什么的例子數(shù)組可以使用如下代碼:
1valgreetStrings=Array("Hello",",","World\n")
這里使用()其實(shí)還是調(diào)用Array類的關(guān)聯(lián)對(duì)象Array的apply方法,也就是
1valgreetStrings=Array.apply("Hello",",","World\n")
第八步: 使用Lists
Scala也是一個(gè)面向函數(shù)的編程語言,面向函數(shù)的編程語言的一個(gè)特點(diǎn)是,調(diào)用某個(gè)方法不應(yīng)該有任何副作用,參數(shù)一定,調(diào)用該方法后,返回一定的結(jié)果,而不會(huì)去修改程序的其它狀態(tài)(副作用)。這樣做的一個(gè)好處是方法和方法之間關(guān)聯(lián)性較小,從而方法變得更可靠和重用性高。使用這個(gè)原則也就意味著就變量的設(shè)成不可修改的,這也就避免了多線程訪問的互鎖問題。
前面介紹的數(shù)組,它的元素是可以被修改的。如果需要使用不可以修改的序列,Scala中提供了Lists類。和Java的List不同,Scala的Lists對(duì)象是不可修改的。它被設(shè)計(jì)用來滿足函數(shù)編程風(fēng)格的代碼。它有點(diǎn)像Java的String,String也是不可以修改的,如果需要可以修改的String對(duì)像,可以使用StringBuilder類。
比如下面的代碼:
1valoneTwo=List(1,2)
2valthreeFour=List(3,4)
3valoneTwoThreeFour=oneTwo:::threeFour
4println (oneTwo +" and "+ threeFour +" were not mutated.")
5println ("Thus, "+ oneTwoThreeFour +" is a new list")
定義了兩個(gè)List對(duì)象oneTwo和threeFour ,然后通過:::操作符(其實(shí)為:::方法)將兩個(gè)列表鏈接起來。實(shí)際上由于List的不可以修改特性,Scala創(chuàng)建了一個(gè)新的List對(duì)象oneTwoThreeFour來保存兩個(gè)列表連接后的值。
List也提供了一個(gè)::方法用來向List中添加一個(gè)元素,::方法(操作符)是右操作符,也就是使用::右邊的對(duì)象來調(diào)用它的::方法,Scala中規(guī)定所有以:開頭的操作符都是右操作符,因此如果你自己定義以:開頭的方法(操作符)也是右操作符。
如下面使用常量創(chuàng)建一個(gè)列表:
1valoneTowThree=1::2::3::Nil
2println(oneTowThree)
調(diào)用空列表對(duì)象Nil的 ::方法 也就是
1valoneTowThree=Nil.::(3).::(2).::(1)
Scala 的List類還定義其它很多很有用的方法,比如 head, last,length, reverse,tail 等這里就不一一說明了,具體可以參考List的文檔。
第九步:使用元組(Tuples)
Scala 中另外一個(gè)很有用的容器類為Tuples,和List不同的Tuples可以包含不同類型的數(shù)據(jù),而List只能包含同類型的數(shù)據(jù)。Tuples在方法需要返回多個(gè)結(jié)果時(shí)非常有用。(Tuple對(duì)應(yīng)到數(shù)學(xué)的矢量的概念)。
一旦定義了一個(gè)元組,可以使用._和索引來訪問員組的元素(矢量的分量,注意和數(shù)組不同的是,元組的索引從1開始)
1valpair=(99,"Luftballons")
2println(pair._1)
3println(pair._2)
元祖的實(shí)際類型取決于它的分量的類型,比如上面pair的類型實(shí)際為 Tuple2[Int,String],而 (‘u’,’r’,”the”,1,4,”me”)的類型為Tuple6[Char,Char,String,Int,Int,String].
目前Scala支持的元祖的最大長(zhǎng)度為22.如果有需要,你可以自己擴(kuò)展更長(zhǎng)的元祖。
第十步: 使用Sets和Maps
Scala語言的一個(gè)設(shè)計(jì)目標(biāo)是讓程序員可以同時(shí)利用面向?qū)ο蠛兔嫦蚝瘮?shù)的方法編寫代碼,因此它提供的集合類分成了可以修改的集合類和不可以修改的集合類兩大類型。比如Array總是可以修改內(nèi)容的,而List總是不可以修改內(nèi)容的。類似的情況,Scala也提供了兩種Sets和Map集合類。
比如Scala API 定義了Set的基Trait類型Set(Trait的概念類似于Java中的Interface,所不同的Scala中的Trait可以有方法的實(shí)現(xiàn)),分兩個(gè)包定義Mutable(可變)和Immutable(不可變),使用同樣名稱的子Trait。下圖為Trait和類的基礎(chǔ)關(guān)系:

使用Set的基本方法如下:
1varjetSet=Set ("Boeing","Airbus")
2jetSet +="Lear"
3println(jetSet.contains("Cessna"))
缺省情況Set為Immutable Set,如果你需要使用可修改的集合類(Set類型),你可以使用全路徑來指明Set,比如 scala.collection.mutalbe.Set 。
Scala提供的另外一個(gè)類型為Map類型,Scala也提供了Mutable和Immutable 兩種Map 類型

Map的基本用法如下(Map類似于其它語言中的關(guān)聯(lián)數(shù)組如PHP)
1valromanNumeral=Map (1->"I",2->"II",
23->"III",4->"IV",5->"V")
3println (romanNumeral(4))
第十一步: 學(xué)習(xí)識(shí)別函數(shù)編程風(fēng)格
Scala語言的一個(gè)特點(diǎn)是支持面向函數(shù)編程,因此學(xué)習(xí)Scala的一個(gè)重要方面是改變之前的指令式編程思想(尤其是來自Java或C#背景的程序員),觀念要想函數(shù)式編程轉(zhuǎn)變。首先在看代碼上要認(rèn)識(shí)哪種是指令編程,哪種是函數(shù)式編程。實(shí)現(xiàn)這種思想上的轉(zhuǎn)變,不僅僅會(huì)使你成為一個(gè)更好的Scala程序員,同時(shí)也會(huì)擴(kuò)展你的視野,使你成為一個(gè)更好的程序員。
一個(gè)簡(jiǎn)單的原則,如果代碼中含有var類型的變量,這段代碼就是傳統(tǒng)的指令式編程,如果代碼只有val變量,這段代碼就很有可能是函數(shù)式代碼,因此學(xué)會(huì)函數(shù)式編程關(guān)鍵是不使用vars來編寫代碼。
來看一個(gè)簡(jiǎn)單的例子:
1defprintArgs ( args:Array[String]):Unit={
2vari=0
3while(i < args.length) {
4println (args(i))
5i+=1
6}
7}
來自Java背景的程序員開始寫Scala代碼很有可能寫成上面的實(shí)現(xiàn)。我們?cè)囍コ齰ars變量,可以寫成跟符合函數(shù)式編程的代碼:
1defprintArgs ( args:Array[String]):Unit={
2for( arg <- args)
3println(arg)
4}
或者更簡(jiǎn)化為:
1defprintArgs ( args:Array[String]):Unit={
2args.foreach(println)
3}
這個(gè)例子也說明了盡量少用vars的好處,代碼更簡(jiǎn)潔和明了,從而也可以減少錯(cuò)誤的發(fā)生。因此Scala編程的一個(gè)基本原則上,能不用Vars,盡量不用vars,能不用mutable變量,盡量不用mutable變量,能避免函數(shù)的副作用,盡量不產(chǎn)生副作用。
第十二步: 讀取文件
使用腳本實(shí)現(xiàn)某個(gè)任務(wù),通常需要讀取文件,本節(jié)介紹Scala讀寫文件的基本方法。比如下面的例子讀取文件的每行,把該行字符長(zhǎng)度添加到行首:
1importscala.io.Source
2if(args.length >0){
3for( line <- Source.fromFile(args(0)).getLines())
4println(line.length +" "+ line)
5}
6else
7Console.err.println("Please enter filename")
可以看到Scala引入包的方式和Java類似,也是通過import語句。文件相關(guān)的類定義在scala.io包中。 如果需要引入多個(gè)類,Scala使用 “_” 而非 “*”.
通過前面兩篇文章的介紹,你應(yīng)該對(duì)Scala編程有了一個(gè)大概的了解,可以編寫一些簡(jiǎn)單的Scala腳本語言。Scala的功能遠(yuǎn)遠(yuǎn)不止這些,在后面的文章我們?cè)谝灰辉敿?xì)介紹。