1. 使用 val & var 定義變量
Scala 中的變量被分為2種:val 和 var。其含義於 Java 中的 final 關鍵字類似。
val 等同於被 final 修飾過的變量, 即一旦初始化便不可被重新賦值。
var 等同於未被 final 修飾過的變量,可以被重新賦值。
1 def main(args: Array[String]): Unit = { 2 val x = 1 3 x = 2 // 編譯錯誤 4 }
1 def main(args: Array[String]): Unit = { 2 var x = 1 3 x = 2 // 編譯成功 4 }
與 Java 相同,這里的所謂不變性,是指引用不可變但是引用對象的狀態卻是可變的。
object Demo { def main(args: Array[String]): Unit = { val a = new A(1) a.x = 3 println(a.x) } } class A(myX: Int) { var x: Int = myX }
以上的例子,輸出結果為3。
2. 類型推斷
可以看出這里有一個很明顯的與 Java 不同的地方:在聲明變量的時候,沒有顯式地給出類型
Scala 支持類型推斷,就是它能夠根據等式右側的值來推斷出這個參數的類型,例如上面的例子,x 的類型就是 scala.Int。
如果這並不是你所希望的 x 類型,例如,你期望初始化一個 Double 類型的,可以這么定義:
val x: Double = 1
需要注意的是,如果只希望聲明變量,而不同時對變量進行初始化(一般來說這種情況只存在於定義成員變量),那么就必須顯式定義類型,因為沒有等式右側去推測這個參數的實際類型。
abstract class MyClass { val y: Int }
3. 強調參數可變性的意義
Scala 之所以使用 val 和 var 來定義參數,主要就是為了在聲明變量時,就確定它的可變性。
Scala 推薦能使用 val 的地方就是用 val,這么做的優勢有:
- Scala 是一門支持函數式編程的語言,將變量定義成 val,可以有效地規避/減少函數的副作用。
- 將成員變量定義成 val,可以保證構造的對象是不可變對象,這個性質在多線程編程中很有用。
關於不可變對象,可以參考《Java 並發編程實戰》P38(因為 Scala 是運行在 JVM 平台上的,編譯之后本質其實就是 Java 的字節碼文件)
當滿足以下條件時,獨享才是不可變的:
- 對象創建以后其狀態就不能改變。
- 對象的所有域都是 final 類型的。
- 對象是正確創建的(在對象創建期間,this 引用沒有溢出)。
4. 使用 def 定義函數
我們觀察以下函數的定義:
def max(x: Int, y: Int): Int = { if (x > y) x else y }
定義一個函數包括以下部分:
- def -> 定義函數的關鍵字
- max -> 函數名
- x: Int, y: Int -> 參數列表
- Int -> 函數的結果類型
- if (x > y) x else y -> 函數體
5. 函數的結果類型
由於 Scala 的類型推斷機制,函數的結果類型是可以省略的,也就是說沒這么定義函數也是可行的:
def max(x: Int, y: Int) = { if (x > y) x else y }
但是,有一種例外,就是遞歸地函數,必須給出結果類型:
def factorial(x: Int) = { require(x >= 0) if (x > 1) x * factorial(x - 1) else 1 // 編譯錯誤 }
雖然如此,但是一般推薦所有可以被外部訪問的函數(等價於 Java 中的 public),都顯式地加上結果類型。
在上面 main 函數的例子中,結果類型 Unit 等價於 Java 中的 Void,表示函數並不會返回有實際意義的結果。