原文地址 本文只是帶你進入 Scala 的世界,包括安裝、不可變變數 val、可變變數 var、定義類、集合(包括列表(list)、集(set)、映射(map))以及集合遍歷和集合庫(能達到並行/併發效果)。 題外話,如果 Java 爭氣的話,還就真不會出現像 Scala 這些語言。對於函數編程風格...
本文只是帶你進入 Scala 的世界,包括安裝、不可變變數 val、可變變數 var、定義類、集合(包括列表(list)、集(set)、映射(map))以及集合遍歷和集合庫(能達到並行/併發效果)。
題外話,如果 Java 爭氣的話,還就真不會出現像 Scala 這些語言。對於函數編程風格的支持,尤其是對於 Lambda 表達式的支持,能夠有助於減少必須要編寫的邏輯無關樣板代碼,也許讓它可以更簡單的關註要面對的任務本身。而 Java 對 Lamdba 表達式的支持到 JavaSE8 才實現(你可以查一下 Java SE8 什麼發佈的,而其他語言何時支持匿名函數、Lambda 表達式、函數式編程、並行編程……)。
Scala,一門強類型定義的靜態類型語言,結合了面向對象編程與函數編程思想,語法簡潔,完全相容Java,運行在 JVM 上。JVM 上的其他語言:Groovy、JRuby、Clojure。那麼 Scala 有什麼不同?能同時提供函數式風格和良好併發支持的強類型語言,只有 Scala。JRuby 和 Groovy 都是動態語言(Scala 是靜態類型語言),它們不是函數式的,也無法提供比 Java 更好的併發解決方案。另一方面,Clojure 是一種混合型的函數式語言,它天生就是動態的,因此不是靜態類型。而且它的語法類似 Lisp,除非你很熟悉,否則這可不是一種易於掌握的語言(Lisp 是號稱高智商的人才能使用的語言,如果你看過《黑客與畫家》,應該記得作者的一句話,大意是,如果競爭對手採用 Lisp 開發 Web,那就應該小心了,言下之意是,Lisp 跟其他語言相比,生產效率太高了,很容易實現一個想法)。
文中代碼本人在 Scala 2.11 上編譯並運行通過。
作為第一步,先安裝好最新的 Scala 發佈包 Typesafe stack,打開命令行視窗,鍵入“scala”:這將啟動 REPL(讀入-運算 輸出 迴圈)互動式編碼環境。然後就可以寫下你的第一行 Scala 代碼:
scala> val columbus: Int = 1492
columbus: Int = 1492
scala>
聲明瞭一個類型為 Int 變數,初始值為 1492,就像在Java里 Int columbus = 1492; 一樣。
Scala 把類型放在變數之後(反向的聲明方式),還使用“val”顯性地把變數聲明為不可變。如果想修改這個變數:
scala> columbus=1500
<console>:8: error: reassignment to val
columbus=1500
^
scala>
錯誤消息精確地指出了錯誤位於行的位置。
再嘗試聲明這個變數,但這次用“var”,讓其可變更。這樣編譯器能推斷出 1492 是一個整數,也就不需要指定類型了:
scala> var columbus = 1492
columbus: Int = 1492
scala> columbus = 1500
columbus: Int = 1500
scala>
接下來,我們來定義一個類,名為 Employee,有三個不可變更的欄位:name、age 和 company,擁有各自的預設值。
scala> case class Employee(name:String="guest",
| age:Int=30,
| company:String="DevCode")
defined class Employee
scala>
關鍵字“case”相當於 Java 里的 switch 語句,只不過更為靈活。它說明該類具有模式匹配的額外機制,以及其他一些特性,包括用來創建實例的工廠方法(不需要使用“new”關鍵字來構造),同樣也不需要創建預設的 getter 方法。與 Java 中不同的是,變數預設下的訪問控制是 public(而不是protected),而Scala為公開變數創建一個 getter 方法,並命名為變數名。如果你願意,你也可以把欄位定義成可變且/或私有(private)的,只需要在參數之前使用“var”(例如:case class Person(private var name:String))。
我們再來用不同方式創建一些實例,看看其他的特性,像是命名參數和預設參數(從Scala2.8開始引入):
scala> val guest=Employee()
guest: Employee = Employee(guest,30,DevCode)
scala> val guestAge=guest.age
guestAge: Int = 30
scala> val anna=Employee("Anna")
anna: Employee = Employee(Anna,30,DevCode)
scala> val thomas=Employee("Thomas",41)
thomas: Employee = Employee(Thomas,41,DevCode)
scala> val luke=Employee("Luke",company="LucasArt")
luke: Employee = Employee(Luke,30,LucasArt)
scala> val yoda=luke.copy("Yoda",age=800)
yoda: Employee = Employee(Yoda,800,LucasArt)
scala>
不過,下麵的寫法是行不通的(可不是因為 Darth 不是 DevCode 的雇員!)
scala> val darth=Employee("Darth","DevCode")
<console>:9: error: type mismatch;
found : String("DevCode")
required: Int
val darth=Employee("Darth","DevCode")
^
scala>
這是由於構造函數在這個位置需要 age 作為參數,因為函數參數沒有顯性地進行命名。
現在我們再來看集合,這才是真正讓人興奮的地方。Scala 主要集合類型包括列表(list)、集(set)和映射(map)。
有了泛型(Java5 以上),Java可以遍歷一個列表,比方說整數型列表,用下麵代碼:
List<Integer> numbers = new arrayList<Integer>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
for(Integer n:numbers) {
System.out.println("Number "+n);
}
運行結果:
Number 1
Number 2
Number 3
Scala 對於可變集合和不可變集合進行了系統性地區別處理,不過,鼓勵使用不可變集合,也因此在預設情況下創建不可變集合。這些集合是通過模擬的方式實現添加、更新和刪除操作,在這些操作中,不是修改集合,而是返回新的集合。
與前面的 Java 代碼等價的 Scala 代碼可能像下麵這樣:
scala> val numbers=List(1,2,3)
numbers: List[Int] = List(1, 2, 3)
scala> for(n<-numbers) println("Number "+n)
Number 1
Number 2
Number 3
scala>
這裡的“for”迴圈語法結構非常接近於 Java 的命令式編程風格。在 Scala(以及 Java 虛擬機上其他很多語言如:Groovy、JRuby 或 JPython)里還有另外一種方式來實現上面的邏輯。這種方式使用一種更加偏向函數編程的風格,引入了 Lambda 表達式(有時也稱為閉包——closure)。簡單地說,Lambda 表達式就是你可以拿來當作參數傳遞的函數。這些函數使用參數作為輸入(在我們的例子中就是“n”整型變數),返回語句作為函數體的最終語句。他們的形式如下:
functionName { input =>
body
}
scala> numbers.foreach{n:Int=> println("Number "+n) }
Number 1
Number 2
Number 3
scala>
上面的例子中,函數體只有一條語句(println……),返回的是單位(Unit,也就是“空結果”),也就是大致相當於 Java 中的 void,不過有一點不同的是——void是不返回任何結果的。
除了列印數值列表外,我們更想做處理和變換這些元素,這時我們需要調用方法來生成結果列表,以便後面接著使用。讓我們嘗試一些例子:
scala> val reversedList=numbers.reverse
reversedList: List[Int] = List(3, 2, 1)
scala> val numbersLessThan3=numbers.filter{n=>n<3}
numbersLessThan3: List[Int] = List(1, 2)
scala> val oddNumbers=numbers.filterNot{n=>n%2==0}
oddNu