1. 幾個概念說明
- 在Scala中,方法與函數幾乎可以等同(定義 使用 運行機制),只是函數的使用方法更加靈活多樣
- 函數式編程是從編程方式的角度來談的。函數式編程把函數當成一等公民,充分利用函數、支持函數的多種使用方式(調用)。既可以作爲函數的參數使用,也可以將函數賦值給一個變量;此外函數的創建不用依賴類或者對象,而在Java中,函數的創建需要依賴類/抽象類或者接口
package com.lineshen.chapter5
object method2FunctionDemo {
def main(args: Array[String]): Unit = {
val dog = new Dog
val res1 = dog.sum(10, 20)
println(res1)
//方法轉成函數
val func = dog.sum _
val res2 = func(20, 30)
println(res2)
//函數編程
val func2 = (n1: Int, n2: Int) => n1 + n2
val res3 = func2(30,40)
println(res3)
}
}
class Dog {
def sum(n1: Int, n2: Int): Int = {
n1 + n2
}
}
>>30
>>50
>>70
函數的基本約定:
def 函數名([參數名:參數類型], ... ) [[:返回類型]] = {
函數體
return 返回值
}
- 函數申明關鍵字是 def
- [參數:參數類型] 表示函數的輸入,可以沒有,如果有多個,用逗號分割
- 函數中的語句是爲了實現某一功能的代碼塊
- 函數可以有返回值,也可以沒有;最好使用=,直接進行返回類型的推導
- 如果沒有 reture ,默認以執行到最後一行的結果作爲返回值
package com.lineshen.chapter5
object FunDemo1 {
def main(args: Array[String]): Unit = {
val n1 = 1
val n2 = 2
println("sum="+getSum(n1,n2,"+"))
}
def getSum(n1: Int, n2: Int,opt: String) = {
if (opt == "+"){
n1 + n2
} else if (opt == "-"){
n1 - n2
} else {
null
}
}
}
本例中,n1+n2 / n1-n2 與 null的類型無法統一,就可以直接使用=進行返回類型的推導。
2. 函數遞歸
函數遞歸需要遵守的重要規則:
- 程序執行函數時,就會創建一個新的受保護的獨立空間(新函數棧)
- 函數的局部變量是相互獨立的,不會相互影響
- 遞歸必須向逼近遞歸的條件逼近,否則就是無限遞歸
3. 函數的注意事項以及使用細節
- 如果函數沒有形參,那麼函數調用過程中,不用寫()
- 形參列表和返回值列表的數據類型可以是值類型,也可以是引用類型
package com.lineshen.chapter5
object funcDetails {
def main(args: Array[String]): Unit = {
val tiger0 = new tiger
println(tiger0.name)
val tiger1 = test(100, tiger0)
println(tiger1.name)
}
def test(n1:Int, tiger0:tiger): tiger= {
println("n1="+ n1)
tiger0.name = "jack"
tiger0
}
}
class tiger{
var name = "lineshen"
}
輸出結果: lineshen 100 jack
- Scala中的函數可以根據函數體最後一行代碼自動推斷函數的返回值類型,該種情況下,return也可以不用寫
- 因爲Scala可以自動推斷返回值類型,因此,直接使用=就好了,或者用[:Any=]
- 一旦使用return,就必須指定返回值類型 [:返回值類型];如果不指定,就是表示該函數沒有任何返回值
- 如果函數返回值制定了Unit,那麼return無效
- Scala支持高級嵌套,如類中可以再一次聲明類;方法中可以繼續定義方法
package com.lineshen.chapter5
object dupFunc {
def main(args: Array[String]): Unit = {
def sayOK(): Unit = { // private final sayOK$1
println("say ok")
def sayOK(): Unit = { // private final sayOK$2
println("say ok, say ok")
}
}
}
}
- 形參可以進行初始化,默認調用;實參會覆蓋形參;可以指定覆蓋特定的形式參數默認值【帶名參數】
- Scala中的形參默認爲是val,函數體中不允許去修改
- 遞歸無法進行類型自動推導,必須指定返回類型
- 支持可變形參,用for進行解析
4. 惰性函數
惰性計算(儘可能延遲表達式求值)是許多函數式編程語言的特性。惰性集合在需要時提供其元素,無需預先計算它們,這帶來了一些好處。首先,可以將耗時的計算推遲到絕對需要的時候。其次,可以創造無限個集合,只要它們繼續收到請求,就會繼續提供元素。函數的惰性使用能夠得到更高效的代碼。Java並沒有爲惰性提供原生支持, Scala提供了,使用很方便。
object layDemo {
def main(args: Array[String]): Unit = {
lazy val res = sum(10, 20)
println("-------------")
println("-------------")
println("-------------")
println(res)
}
def sum(n1: Int, n2: Int): Int = {
n1 + n2
}
輸出結果(lazy函數只能用val進行修飾,爲了保證線程安全):
-------------
-------------
-------------
30
5. 異常處理
- 拋出異常
Scala 拋出異常的方法和 Java一樣,使用 throw 方法,例如,拋出一個新的參數異常:
throw new IllegalArgumentException
- 捕捉異常
異常捕捉的機制與其他語言中一樣,如果有異常發生,catch字句是按次序捕捉的。因此,在catch字句中,越具體的異常越要靠前,越普遍的異常越靠後。 如果拋出的異常不在catch字句中,該異常則無法處理,會被升級到調用者處。
捕捉異常的catch子句,語法與其他語言中不太一樣(java使用了try-catch-catch-finnaly 無論有沒有異常 finally代碼塊都會執行,一般用來釋放資源)。在Scala裏,借用了模式匹配的思想來做異常的匹配,因此,在catch的代碼裏,是一系列case字句,如下例所示:
import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
object exceptionDemo {
def main(args: Array[String]) {
try {
val f = new FileReader("input.txt")
} catch {
case ex: FileNotFoundException => {
println("Missing file exception")
}
case ex: IOException => {
println("IO Exception")
}
}finally{
println("Programming has run")
}
}
}
輸出結果:
Missing file exception
Programming has run 【實際應用中,一般用於釋放資源】