衆所周知,scala一貫宣稱本身是面向函數的編程,(java表示不服,我是面向bean的編程!)那什麼是函數?java
在接觸java的時候,有時候用函數來稱呼某個method(實在找不出詞了),有時候用方法來稱呼某個method,雖然method的中文翻譯就是「方法」,但對於java來講,方法和函數是等價的,或者說沒有函數這個概念。編程
而對於scala,這二者彷佛有一個較爲明確的邊界。閉包
你會發現滿世界的函數,而你卻在寫方法app
Scala的方法和java能夠當作是同樣的,只是多了點語法糖。ide
好比無參方法在申明時能夠不加括號,甚至在調用過程也不用加括號函數
def f = 1+1 println(f)
好比方法能夠添加泛型規則,這在java中只能在類申明ui
def f[T](t: T) = {t}
還有其它不少細節語法,遇到才深刻吧spa
通常而言只要知道函數的結構就行(可是我想說,spark的代碼就沒有一個函數長成這樣的啊..),請忽略下圖的「函數」字樣,其實就是方法scala
def method(): Unit ={ //本地方法 def print(str:String): Unit ={ println(str) } print("hello") }
方法的語法仍是跟java差很少的,只是有些能夠省略而已。翻譯
比較重要的就是本地方法,即方法中嵌套方法
Scala的函數是基於Function家族,0-22,一共23個Function Trait能夠被使用,數字表明瞭Funtcion的入參個數
下面這四個函數的意義是同樣的
// println(fun1) // println(fun2) // println(fun3) // println(fun4) // 都爲<function2> val fun1 = new Function2[Int,Int,Int]() { override def apply(v1: Int, v2: Int): Int = { v1+v2 } } val fun2 = new ((Int, Int) => Int)() { override def apply(v1: Int, v2: Int): Int = { v1+v2 } } val fun3 = (v1:Int,v2:Int) => v1+v2 // _能夠把method轉換成function val fun4 = fun4Method _ def fun4Method(v1:Int,v2:Int): Int = { v1+v2 }
通常咱們都採用第三種fun3定義方式,也是最難懂的一個定義方式。具體結構參考下圖
那函數有什麼用呢?
Java裏只有方法都能適應一切需求,那scala又提出函數的概念確定有意義。
1.函數能夠直接賦值給變量,可讓函數很方便的傳遞
2.閉包(closure),能夠把靈活操做代碼塊,從而引伸出其餘靈活的語法
在spark中,有不少方法入參中使用函數的場景,好比以下函數
defrunJob[T,U](fun: Iterator[T] => U ,resHandler: (Int, U) => Unit): Unit ={ //忽略裏面的邏輯 }
其中的fun和resHandler都是函數
Fun是入參爲Iterator[T],返回值爲U的函數,一個入參的函數其實就是Function1的實例
resHandler是入參爲Int和 U無返回值的函數,二個入參的函數其實就是Function2
模擬spark中常見的一段代碼語法,拿一個普通scala類型的例子來講
//模擬spark的runJob方法 def runJob[T,U](fun: Iterator[T] => U ,resHandler: (Int, U) => Unit): Unit ={ val listBuffer = new ListBuffer[T] listBuffer.append("h".asInstanceOf[T]) listBuffer.append("e".asInstanceOf[T]) listBuffer.append("l".asInstanceOf[T]) listBuffer.append("l".asInstanceOf[T]) listBuffer.append("o".asInstanceOf[T]) //這裏調用函數其實用到了伴生對象的概念,fun(xxx)就是fun.apply(xxx) val res = fun(listBuffer.iterator) //spark中,這裏是每一個partition的數據都存入arr,這裏作模擬就一個partition了:) resHandler(0,res) } //模擬調用runJob的方法 def main(args: Array[String]): Unit = { val arr = new Array[String](1) //fun函數的實際邏輯 val fun = (it:Iterator[String]) => { val sb = new StringBuilder() while (it.hasNext) sb.append(it.next()) sb.toString() } //resHandler函數的實際邏輯 val resHandler = (i:Int,res:String) => arr(i) = res runJob[String,String](fun ,resHandler) println(arr.mkString("")) }
其實就是傳遞函數的邏輯,和java的匿名類差很少(只有一個方法的匿名類),只是多了點語法糖
這麼作的好處也是不言而喻的
1.能夠構造出更抽象的方法,使得代碼結構更簡潔
2.spark的思想就是lazy,而函數傳遞也是一個lazy的過程,只有在實際觸發纔會執行
英文爲PartialFunction,不知道這麼翻譯對不對,貌似都這麼叫。
PartialFunction實際上是Funtion1的子類
參考源碼
trait PartialFunction[-A, +B] extends (A => B)
A => B就是標準的函數結構
那PartialFunction有什麼做用呢?
模式匹配!
PartialFunction最重要的兩個方法,一個是實際的操做邏輯,一個是校驗,其實就是用來作模式匹配的。
《Scala編程》