Scala的if/else語法結構和Java同樣。不過,在Scala中if/else表達式有值,這個值就是跟在if或else以後的表達式的值。例如:java
if(x>0) 1 else -1
或者將表達式的值賦值給變量:程序員
val s = if(x>0) 1 else -1
這與以下語句的效果同樣:數組
if(x>0) s = 1 else s = -1
不過,第一種寫法更好,由於它能夠用來初始化一個val常量。而第二種寫法中,s必須是var變量。安全
--------------------------------函數
在Scala中if/else有着和Java或者C++的三元表達式相同的功能,例如this
x>0 ? 1 : -1 //Java或C++
而在Scala中,咱們能夠這樣:編碼
if(x>0) "hello" else -1
在Scala中每一個表達式都有一種類型,表達式的類型能夠相同能夠不相同,這點與Java有所不一樣。不一樣類型表達式的if/else咱們稱爲混合表達式,spa
接收他們的值或變量的類型爲公共類型Any(至關於Java中的Object)線程
--------------------------------scala
若是else部分缺失了,例如:
if(x>0) 1
那麼可能if沒有輸出值,可是在Scala中,每一個表達式都應該有值。這個問題的解決方案是引入一個Unit類,寫作()。不帶餓死了的這個if語句等同於
if(x>0) 1 else ()
你能夠把()當作「無有用值」的佔位符,將Unit當作Java或C++中的void。
注:Scala沒有switch語句
注:REPL(解析器)比起編譯器來講,它只可以按行執行程序,例如:
if (x > 0) 1 else if (x == 0) 0 else -1
這時REPL會執行if (x>0) 1 而後顯示結果。以後的else他將會報錯。
若是你想在else前換行的話,要用花括號:
if (x > 0) {1 }else if (x == 0) 0 else -1
只有在REPL中才會有這樣的顧慮,其餘地方你們大可沒必要擔憂
提示:若是想在REPL中粘貼成塊的代碼又不想讓REPL按行執行,可使用粘貼模式。鍵入
:paste
把代碼粘貼進去,而後Ctrl+D。這樣REPL就會把代碼塊當作一個總體分析。
在Java和C++中,每一個語句都要以分號結束。而如今Scala中與JavaScript和其餘腳本相似,行位不須要設置分號。一樣在}、else以及相似的位置也沒必要寫分號,只要可以從上下文明確的判斷出這裏語句是終止的便可。
不過,若是你想在單行中寫下多個語句,就須要將他們以分號分開。例如:
if(n > 0) { r = r * n; n -=1 }
若是你在寫一個較長的語句,須要分兩行寫的話,就須要確保第一行以一個不能用做語句結尾的符號結尾。一般來講一個比較好的選擇是操做符:
s = s0 + (v - v0) * t + // +來告訴解析器這裏不是語句的末尾 0.5 * (a - a0) * t * t
在實際的編碼時,長表達式一般涉及函數或方法調用,Scala程序猿更傾向於使用Kernighan&Ritchie風格的花括號
if(n >0){ r = r * n n -= 1 }
以‘{’結束的行很清楚的表達了後面還有內容,直到碰見匹配的‘}’
許多來自Java或C++的程序員一開始並不適應省去分號的作法。若是你更傾向於使用分號,用了就是了,沒啥壞處。
在Java或C++中,塊語句是包含於{}中的語句序列。每當你須要在邏輯分支或循環中放置多個動做時,你均可以使用塊語句。
在Scala中,{}塊包含一系列的表達式,其中結果也是一個表達式。塊中最後一個表達式的值就是塊兒的值。
--------------------------------
在Scala中,賦值動做自己是沒有值得——或者,更嚴格地說,他們的值是Unit類型的。你應該記得,Unit類型等同於Java和C++中的void,而這個類型的值只有一個值,寫作()。
一個賦值語句結束的塊,好比
{r = r * n; n -= 1}
的值是Unit類型的。這沒有問題,只是當咱們定義函數時須要意識到這一點。
注:因爲賦值語句的值是Unit類型的,別把他們串接在一塊兒。
x = y = 1 //別這麼作
y = 1 的值是(),你不可能想把一個Unit類型的值賦值給x。(在Java或者C++中,上面的作法是能夠作到x、y同時賦值的)
若是要打印一個值,咱們用print或者println以及printf函數。與Java中的用法相同,這裏不作過多解釋。
--------------------------------
你能夠用readLine函數從控制檯讀取一行輸入。若是要讀取數字、Boolean或者是字符,能夠用readInt、readDouble、readByte、readShort、readLong、readFloat、readBoolean或者readChar。
與其餘方法不一樣,readLine帶一個參數做爲提示字符串:
Scaca中擁有與Java和C++相同的while和do循環。例如:
--------------------------------
Scala中沒有與Java相同的for結構:for(初始化變量;檢查變量是否知足條件;更新變量)。
在Scala中只有這樣的for循環語法:
for(i ← 表達式) //i不須要定義,也能夠因此取名
接下來咱們用RichInt類的to方法,去演示Scala中for的用法:
還有另外一種遍歷方法until,它經常使用於遍歷字符串或者數組,同上面的to方法的例子比較,能看出他們兩個的區別
to方法:包含頭和尾的遍歷(至關於<=)
until方法:包含頭不包含尾的遍歷(至關於<)
注:遍歷字符串的兩種方法:
1.用until方法
2.直接遍歷
--------------------------------
Scala中並無提供break或者continue語句來退出循環。若是須要break時咱們能夠採起以下選項:
break方法使用示例:
Scala中的for循環要比Java功能豐富的多,好比:
1.咱們能夠在for循環中添加多個 變量←表達式 這種形式的生成器,用分號分隔開:
2.每個生成器均可以帶一個守衛,以if開頭的Boolean表達式:注意if前沒有分號
3.你可使用任意多的定義,引入能夠在循環中使用的變量
4.若是for循環以yield開始,則該循環會構造出一個集合,每次迭代生成集合中的一個值
這類循環叫作for推導式,for推導式生成的集合與它的第一個生成器的類型是兼容的
Scala除了方法還支持函數,方法對對象進行操做,函數不是。C++也有函數,在Java中咱們只能經過靜態方法來模擬函數。
要定義函數,完整格式以下:
def [函數名稱]([參數名稱]:[參數類型]...): [返回值類型] = {
函數體
}
例如:
def add(x : Int , y : Int) : Int = { var z = x + y; return z; }
因爲{}塊中最後一個表達式的值就是塊兒的值。所以咱們能夠省略return。
def add(x : Int , y : Int) : Int = { var z = x + y; z; }
在進一步簡化:
def add(x : Int , y : Int) : Int = x + y
在Scala中,只要函數不是遞歸的,咱們就不須要指定返回類型。Scala編譯器能夠經過 = 右側的表達式類型推斷出返回值類型。所以最終函數簡化以下:
def add(x : Int , y : Int) = x + y
若是是遞歸函數,咱們就必需要指定返回值類型了。
def fac(n : Int) : Int = if(n <= 0) 1 else n * fac( n - 1)
說明:雖然在帶名函數中使用return並無什麼不對,可是咱們最好適應沒有return的日子。很快,你就會使用大量的匿名函數,這些函數中return並不返回值給調用者。
它跳出到包含它的帶名函數中。咱們能夠把return當作是函數版break語句,盡在須要時使用。
咱們在調用某些函數時並不顯示的給出全部參數值,對於這些函數咱們可使用默認參數值。例如:
def decorate(prefix : String, str : String, suffix : String) = { prefix + str + suffix }
在調用上面的decorate函數的時候咱們必須給出全部的參數,不然函數將會執行錯誤,那若是此時咱們只想給出一個參數就能讓其運行咱們該如何作呢?那就是默認參數值:
def decorate(prefix : String = "[[", str : String, suffix : String = "]]") = { prefix + str + suffix }
咱們能夠在定義函數入參時先給其賦值,從而作到默認參數值的功能,可是另外一個問題又來了,做爲程序員咱們都知道調用方法或者函數的時候,參數都是按順序寫入的,咱們如何作到給指定位置的入參賦值呢?Scala幫咱們作到了這一點:
如圖,咱們能夠向表達式同樣爲指定參數賦值,這樣咱們就能夠無視參數的順序來調用函數或者方法了,這就是帶名參數
咱們知道在Java中,邊長參數用 ... 來表示:
public void method(int a ...){
方法體...
}
而在Scala中,邊長參數用* 來表示,好比咱們建立一個計算多個數相加的方法,參數不肯定。
def add(a : Int*) = { var sum = 0; for(x <- a) sum += x; sum; }
對於上面的add方法,若是你已經有了一個有值序列,則不能直接將它傳入上面的大的方法,好比:
val a = add(1 to 5) //錯誤
由於咱們的add方法的入參是Int類型的序列,而1 to 5 的類型爲:
他並非add方法的入參類型所以直接傳入必定有問題,須要進行類型的轉換,那麼範圍類型如何轉換成入參序列呢?Scala中爲咱們提供了一種語法,追加 :_*
val a = add(1 to 5 :_* )
兩種狀況對比:
在遞歸的定義中咱們會用到上述語法:
def recursiveSum(args : Int*): Int = { if(args.length == 0) 0 else args.head = recursiveSum(args.tail:_*) }
在這裏,序列的head是他的首個元素,而tail是全部其餘元素的序列,這又是一個序列,咱們用 :_* 來將它轉換成參數序列。
Scala對於不返回值得函數有特殊的表示法。若是函數體包含在花括號當中但沒有前面的=號,那麼返回類型就是Unit。這樣的函數被稱爲過程。過程不返回值,咱們調用它僅僅是爲了他的輔助做用。
例如:
def out(a : Int){ print(a) }
或者咱們能夠顯示聲明Unit返回類型:
def out(a : Int): Unit = { print(a) }
當val 被聲明爲lazy時,他的初始化將被延遲,也就是懶加載,知道咱們首次對它進行取值,例如
//在words被定義時取值 val words = scala.io.Source.fromFile("C:\\Users\\Administrator\\Desktop\\wordCount.txt").mkString //在words被首次使用時取值 lazy val words = scala.io.Source.fromFile("C:\\Users\\Administrator\\Desktop\\wordCount.txt").mkString //在每一次words被使用時取值 def words = scala.io.Source.fromFile("C:\\Users\\Administrator\\Desktop\\wordCount.txt").mkString
你能夠把懶值當作是介於val和def的中間狀態
--------------------------------
說明:懶值並非沒有額外開銷。咱們每次訪問懶值,都會有一個方法被調用,而這個方法將會以線程安全的方式檢查該值是否已被初始化。
Scala的異常工做機制和Java或者C++同樣。拋出異常時你能夠這樣:
throw new Exception("this is a exception")
和Java同樣,排出的對象必須是java.lang.Throwable的子類。不過,與Java不一樣的是,Scala沒有「受檢」異常,你不須要聲明說函數或方法可能拋出某種異常。
--------------------------------
throw表達式有特殊的返回類型Nothing。好比:
if(x >= 0){ sqrt(x) }else throw new IllegalArgumentException(" x should not be negative")
第一個分支類型爲Double,第二個分支類型爲Nothing。所以,if/else表達式的類型是Double
--------------------------------
捕獲異常的語法採用的是模式匹配的語法
try{ "Hello".toInt } catch { case _ : Exception => println("xxxxxxx") case ex : IOException => ex.printStackTrace() }
和Java或C++同樣,更通用的異常應該排在更具體的異常以後。
若是你不須要使用捕獲的異常對象,可使用_來代替變量名。
--------------------------------
try/finally語句讓你能夠釋放資源,不論有沒有發生異常。這點和Java同樣。
咱們也能夠下車和Java中同樣的try/catch/finally語句:
try{...}catch{...}finally{...}