Scala 基礎(1)—— 定義變數 & 定義函式
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 3x = 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,表示函式並不會返回有實際意義的結果。