變數、常數


如果你要宣告一個變數(Variable),在Scala中可以使用varval來宣告,底下是個用val宣告的例子:
val sum: Int = 1 + 2
val text: String = "Hello"
val scores: Array[Int] = Array(80, 90, 100)

Scala是個靜態(static type)語言,由變數帶有型態資訊,以上的示範中,sum是變數名稱,Int是其型態,text是變數名稱,型態為String,而scores是變數名稱,型態為Array[Int](陣列中內含Int元素)。

這冗長的宣告方式確實是宣告變數的完整型式,但事實上,它可以使用以下的方式宣告:
val sum = 1 + 2
val text = "Hello"
val scores = Array(80, 90, 100)

畢竟從值的指定來看,1 + 2結果會是Int,"Hello"則是個String,第三行也可以知道是個Array。在某些情況下,若Scala可以由陳述句中,推斷出變數的型態,則無需在變數上重複宣告類型,這是Scala的類型推斷(type inference)特性。

在Scala中,由val宣告的變數,一旦指定值,就不可以再重新指定別的值給它,例如以下會有錯誤訊息:
scala> val sum = 1
sum: Int = 1

scala> sum = 2
<console>:5: error: reassignment to val
       sum = 2
           ^

scala>


如果你希望所宣告的變數在指定值之後,稍後在其它地方也可以再設定新的值給它,則要使用var宣告:
scala> var sum = 1
sum: Int = 1

scala> sum = 2
sum: Int = 2

scala>


Scala鼓勵你使用val來宣告變數,Scala認為在演算流程中修改變數的值,會造成程式邏輯的複雜性與不易維護,使用var宣告變數時通常就是一種訊號,表示你可能使用了 imperative 風格的撰寫方式,Scala鼓勵你使用 functional 風格的撰寫方式,也支援撰寫functional風格程式碼的一些特性(不可變動物件、函式物件、模式比對等,之後會一一介紹)。

使用var宣告值,除了在類別定義中作為資料成員外(預設為null),都必須明確給定值給它,否則會發生錯誤,若不知道值為何,至少要設為null:
scala> var x;
<console>:1: error: '=' expected but ';' found.
       var x;
            ^

scala> var x = null
x: Null = null

scala>


當你將長度較小的資料型態(例如Int)指定給長度較大的資料型態(例如Long),這樣的指定在精確度並不會失去,所以這樣的指定是可行的,但你將長度較大的指定給較小的,就會發生問題:
scala> var x = 1
x: Int = 1

scala> var y = 2L
y: Long = 2

scala> x = y
<console>:6: error: type mismatch;
 found   : Long
 required: Int
       x = y
           ^

scala> y = x
y: Long = 1

scala>


在Scala中要進行轉型(Cast),可以使用asInstanceOf,但對於數值,更方便的作法是直接呼叫toXXX方法,例如:
scala> var x = 1
x: Int = 1

scala> var y = 2L
y: Long = 2

scala> x = y.asInstanceOf[Int]
x: Int = 2

scala> x = y.toInt
x: Int = 2

scala>


有個有趣的事是,Scala在宣告變數名稱時,可以使用Unicode編碼(雖然不鼓勵這麼用),例如:
scala> val \u0073\u0075\u006D = 10
sum: Int = 10

scala>


在Scala中,建議使用駝峰式(Camel case)的命名方式,也就是變數名稱首字小寫,之後每遇到單字首字大寫,例如像scoreOfStudent這樣的名稱。

在Scala中雖然可以使用_作為變數名稱的字元,例如score_of_student這樣的名稱,但不建議使用_作為名稱的一部份,因為在Scala的處理中,會使用一些有底線字元的名稱,如果你的變數名稱湊巧使用了相同的名稱,使得Scala混淆了,可能會有一些無法預期的錯誤。

同樣的,在Scala中也可以使用$作為名稱的一部份,但不建議使用,因為$是Scala處理中一些內部名稱的表示方式(例如編譯器產生的識別字)。

再來看常數(Constant)的定義,常數顧名思義就是不變的數,不過若你要在Scala中定義一個常數,不僅僅是使用val,val所宣告的變數,其意義其實是指設定值給它後,就不能再指定值給它,但還是個變數,例如:
def doSome(x: Int) {
    println(x)
}

上 面這個例子,在Scala中定義一個函式(function),其中x是呼叫時接收引數的參數名稱,在Scala中參數一定是val(只是你不用特別加上 val宣告,不過要加也可以,只是寫來冗長)。但事實上,每次你呼叫函式傳入引數不同,x的值也就不同,所以它還是變數(只是在doSome函式中不可重 新指定值)。

常數的定義,是指那些恒常不變的值,至少在整個程式運行過程中是如此,例如圓周率就是3.141596...這樣的值,或者是你在程式運行過程中絕不改變的值。在Scala中,對於常數值,所採用的命名慣例是首字大寫,之後可以是的駝峰式寫法,例如scala.Math中就定義了Pi表示圓周率,也可以是像scala.Math中MAX_INT這樣的寫法,這比較符合Java開發人員的慣例,不過Scala鼓勵使用首字大寫的駝峰式寫法(其實這不僅是慣例,在模式比對時,有利用到這樣的常數寫法之特性)。

在Scala中,還可以將變數名稱包括在`與`(標記號,也就是~符號鍵下方看似單引號的符號)之間,例如:
val `x` = 10

這個機制的利用時機,可以像是這樣:
scala> val yield = 10
<console>:1: error: illegal start of simple pattern
       val yield = 10
           ^

scala> val `yield` = 10
yield: Int = 10

scala> `yield`
res4: Int = 10

scala>

yield在Scala中是保留字,如果你非得在Scala中使用yeild這個名稱,就可以使用``來包括它。例如以下的範例中,你不可以直接呼叫Thread.yield(),而要改用Thread.`yield`():
scala> Thread.yield()
<console>:1: error: identifier expected but 'yield' found.
       Thread.yield()
              ^

scala> Thread.`yield`()

scala>