Scala進階之路-Scala高級語法之隱式(implicit)詳解
作者:尹正傑
版權聲明:原創作品,謝絕轉載!否則將追究法律責任。
我們調用別人的框架,發現少了一些方法,需要添加,但是讓別人為你一個人添加是不現實的,因此很多很多時候需要我們自己動手。掌握implicit的用法是閱讀Spark源碼的基礎,也是學習Scala其它的開源框架的關鍵,implicit可分為隱式參數,隱式轉換類型以及隱式類三種類型。
一.Scala中的隱士參數
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 4 EMAIL:y1053419035@qq.com 5 */ 6 package cn.org.yinzhengjie.scalaImplicit 7 8 object ScalaImplicit { 9 10 /** 11 * 定義一個隱式值,編譯器在查找隱式值的時候,不能出現歧義,也就是說,編譯器在編譯代碼的時候,不能找到 12 * 兩個類型一致的隱式值,不然編譯器是會編譯不通過的! 13 */ 14 implicit val default:Int = 500 15 16 /** 17 * @param Name : 此處我們將Name設置為隱士的參數 18 */ 19 def sayHello(implicit Name:String = "YinZhengJie") = { 20 println(s"I'm ${Name},I love Beijing !") 21 } 22 23 /** 24 * @param x : 柯里化函數(Currying)的第一個參數 25 * @param y :柯里化函數(Currying)的第二個參數,其類型為一個隱式的參數喲 26 * @return : 返回值類型為Int 27 */ 28 def add(x:Int)(implicit y:Int):Int={ 29 x + y 30 } 31 32 /** 33 * 方法的參數如果有多個隱式參數的話,只需要使用一個implicit關鍵字即可,隱式參數列表必須放在方法的參數列表后面 34 */ 35 def sum(a:Int)(implicit b:Int,c:Int):Int={ 36 a + b + c 37 } 38 39 def main(args: Array[String]): Unit = { 40 41 sayHello("yinzhengjie") 42 43 /** 44 * sayHello方法參數是隱式參數,如果你沒有給sayHello傳遞參數的話,編譯器在編譯的時候會自動從當前的上下文中 45 * 找一個隱式值(符合參數類型的隱式值),如果有則使用,如果沒有就使用sayHello方法參數的默認值,指的注意的是,當一 46 * 各類中出現了多個implicit變量時,貌似默認值也不好使了!當前的類我就定了2個implicit變量就是一個很好的例子! 47 */ 48 // sayHello 49 50 implicit val msg:String = "尹正傑" 51 sayHello 52 53 /** 54 * 在調用柯里化函數(Currying)的時候,我們僅僅出入了第一個參數,第二個參數默認就是類中的成員變量用implicit修飾的default的值。 55 */ 56 val res = add(20) 57 println(s"res =====》 ${res}") 58 59 60 /** 61 * 本來需要傳入3個參數的,但是我們就傳了一個,其他兩個參數就會默認使用類中的成員變量用implicit修飾的default的值。 62 */ 63 val res2 = sum(10) 64 println(s"res2 =====》 ${res2}") 65 } 66 } 67 68 69 70 /* 71 以上代碼輸出結果如下: 72 I'm yinzhengjie,I love Beijing ! 73 I'm 尹正傑,I love Beijing ! 74 res =====》 520 75 res2 =====》 1010 76 */
二.Scala中的隱式類型轉換
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 4 EMAIL:y1053419035@qq.com 5 */ 6 package cn.org.yinzhengjie.scalaImplicit 7 8 object ScalaImplicit { 9 10 /** 11 * 使用關鍵字implicit定義一個隱式方法。 12 */ 13 implicit def doubleToInt(double: Double):Int = { 14 println("====== 調用了方法:doubleToInt ======") 15 double.toInt 16 } 17 /** 18 * 使用關鍵字implicit定義一個隱式函數。 19 */ 20 implicit val doubleToInt2 = (double:Double) => { 21 println("====== 調用了函數:doubleToInt2 ======") 22 double.toInt 23 } 24 25 26 def main(args: Array[String]): Unit = { 27 28 /** 29 * year是一個Int類型的變量,但是賦值的確實一個浮點型數字,此刻編譯器會在當前上下文中找一個 30 * 隱式轉換,找一個能把浮點型變成Int類型的隱式轉換的函數或者方法,如果有函數實現了這個功能就優先 31 * 調用函數的功能,若沒有函數實現該功能就回去找是否有方法實現了該功能!如果函數或者方法都沒有實現 32 * 該功能編譯器是會報錯的! 33 */ 34 val year:Int = 2018.7 35 println(s"year的結果是: ${year}") 36 } 37 } 38 39 40 /* 41 以上代碼輸出結果如下: 42 ====== 調用了函數:doubleToInt2 ====== 43 year的結果是: 2018 44 */
三.Scala中的隱式類
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 4 EMAIL:y1053419035@qq.com 5 */ 6 package cn.org.yinzhengjie.scalaImplicit 7 8 import java.io.File 9 10 import scala.io.Source 11 12 13 14 object ScalaImplicit { 15 /** 16 * 定義隱式類,只能在靜態對象(使用Object修飾)中使用 17 */ 18 implicit class FileRead(file:File){ 19 def myRead() = Source.fromFile(file).mkString 20 } 21 22 def main(args: Array[String]): Unit = { 23 val file = new File("D:\\10.Java\\IDE\\yhinzhengjieData\\Scala\\1.txt") 24 val lineCounts = file.myRead() 25 println(s"file文件對象里面的內容如下:\n${lineCounts}") 26 } 27 } 28 29 30 /* 31 以上代碼輸出結果如下: 32 file文件對象里面的內容如下: 33 hello wolrd 34 hello wolrd 35 hello wolrd 36 hello wolrd 37 hello wolrd 38 hello wolrd 39 hello wolrd 40 hello wolrd 41 hello wolrd 42 hello wolrd 43 hello wolrd 44 hello wolrd 45 hello wolrd 46 hello wolrd 47 hello wolrd 48 hello wolrd 49 hello wolrd 50 hello wolrd 51 */
四.小試牛刀(封裝File對象,新增lineCount方法,用來統計文件的行數,要求使用隱式方法)
1 /* 2 @author :yinzhengjie 3 Blog:http://www.cnblogs.com/yinzhengjie/tag/Scala%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 4 EMAIL:y1053419035@qq.com 5 */ 6 package cn.org.yinzhengjie.scalaImplicit 7 8 import java.io.{BufferedReader, File, FileReader} 9 10 11 /** 12 * 自定義一個RichFile類,封裝File類 13 */ 14 class RichFile(file:File){ 15 /** 16 * 定義方法返回文件的記錄行數 17 */ 18 def linesCount():Int={ 19 val fileReader = new FileReader(file) 20 val bufferReader = new BufferedReader(fileReader) 21 22 var sum = 0 23 try { 24 var line = bufferReader.readLine() 25 while (line != null) { 26 sum += 1 27 line = bufferReader.readLine() 28 } 29 } catch { 30 case _: Exception => sum 31 } finally { 32 fileReader.close() 33 bufferReader.close() 34 } 35 sum 36 } 37 } 38 39 40 object ScalaImplicit { 41 /** 42 * 定義一個隱式方法,將File類型轉換成RichFile類型。 43 */ 44 implicit def fileToRichFile(file: File) = { 45 new RichFile(file) 46 } 47 48 def main(args: Array[String]): Unit = { 49 val file = new File("D:\\10.Java\\IDE\\yhinzhengjieData\\Scala\\1.txt") 50 51 val lineCounts = file.linesCount() 52 println(s"file文件對象里面的行數是:${lineCounts}") 53 } 54 } 55 56 57 /* 58 以上代碼輸出結果如下: 59 file文件對象里面的行數是:18 60 */