先看一個案例演示,引出隱式轉換的實際須要=>指定某些數據類型的相互轉化mysql
object boke_demo01 { def main(args: Array[String]): Unit = { val num: Int = 3.5 //?錯 高精度->低精度 println(num) } }
隱式轉換函數是以implicit關鍵字聲明的帶有單個參數的函數,這種函數將會自動應用,將值從一種類型轉換爲另外一種類型sql
使用隱式函數能夠優雅的解決數據類型轉換函數
案例演示scala
object boke_demo01 { def main(args: Array[String]): Unit = { //編寫一個隱式函數轉成 Double->Int 轉換 //隱式函數應當在做用域才能生效 implicit def f1(d: Double): Int = { //底層 生成 f1$1 d.toInt } implicit def f2(f: Float): Int = { f.toInt } //這裏咱們必須保證隱式函數的匹配只能是惟一的. // implicit def f3(f1:Float): Int = { // f1.toInt // } val num: Int = 3.5 // 底層編譯 f1$1(3.5) val num2: Int = 4.5f // println("num =" + num) } }
-反編譯後的代碼對象
1) 隱式轉換函數的函數名能夠是任意的,隱式函數與函數名無關,只與函數簽名(函數參數類型和返回值類型)有關blog
2) 隱式函數能夠有多個(即:隱式函數列表),可是須要保證在當前環境下,只有一個隱式函數能被識別遞歸
使用隱式轉換方式動態的給MySql類增長delete方法ci
object boke_demo01 { def main(args: Array[String]): Unit = { //編寫一個隱式函數,豐富mySQL功能 implicit def addDelete(msql: MySQL): DB = { new DB } //建立mysql對象 val mySQL = new MySQL mySQL.insert() mySQL.delete() // 編譯器工做 分析 addDelete$1(mySQL).delete() mySQL.update() } } class MySQL { def insert(): Unit = { println("insert") } } class DB { def delete(): Unit = { println("delete") } def update(): Unit = { println("update") } }
隱式值也叫隱式變量,將某個形參變量標記爲implicit,因此編譯器會在方法省略隱式參數的狀況下去搜索做用域內的隱式值做爲缺省參數作用域
object boke_demo01 { def main(args: Array[String]): Unit = { implicit val str1: String = "Jack" //這個就是隱式值 //implicit name: String :name就是隱式參數 def hello(implicit name: String): Unit = { println(name + " hello") } hello //底層 hello$1(str1); } }
//小結 //1. 當在程序中,同時有 隱式值,默認值,傳值 //2. 編譯器的優先級爲 傳值 > 隱式值 > 默認值 //3. 在隱式值匹配時,不能有二義性 //4. 若是三個 (隱式值,默認值,傳值) 一個都沒有,就會報錯 object boke_demo01 { def main(args: Array[String]): Unit = { // 隱式變量(值) // implicit val name: String = "Scala" //implicit val name1: String = "World" //隱式參數 def hello(implicit content: String = "jack"): Unit = { println("Hello " + content) } //調用hello hello //當同時有implicit 值和默認值,implicit 優先級高 def hello2(implicit content: String = "jack"): Unit = { println("Hello2 " + content) } //調用hello hello2 //說明 //1. 當一個隱式參數匹配不到隱式值,仍然會使用默認值 implicit val name: Int = 10 def hello3(implicit content: String = "jack"): Unit = { println("Hello3 " + content) } //調用hello hello3 // hello3 jack // //當沒有隱式值,沒有默認值,又沒有傳值,就會報錯 // def hello4(implicit content: String ): Unit = { // println("Hello4 " + content) // } //調用hello // hello4 // hello3 jack } }
在Scala2.10後提供了隱式類,可使用implicit聲明類,隱式類很是強大,一樣能夠擴展類的功能,比前面使用隱式轉換豐富類庫功能更加的方便,在集合中隱式類會發揮重要做用編譯器
1) 其所帶的構造參數有且只能有一個
2) 隱式類必須被定義在「類」或「伴生對象」或「包對象」裏,即隱式類不能是頂級的(top-level objects)
3) 隱式類不能是case class(樣例類)
4) 做用域內不能有與之相同名稱的標識符
隱式類的案例,進一步認識隱式類
object boke_demo01 { def main(args: Array[String]): Unit = { //DB會對應生成隱式類 //DB是一個隱式類, 當咱們在該隱式類的做用域範圍,建立MySQL實例 //該隱式類就會生效, 這個工做仍然編譯器完成 //看底層.. implicit class DB(val m: MySQL) { //boke_demo01$DB$1 def addSuffix(): String = { m + " scala" } } //建立一個MySQL實例 val mySQL = new MySQL mySQL.sayOk() mySQL.addSuffix() //研究 如何關聯到 DB$2(mySQL).addSuffix(); implicit def f1(d: Double): Int = { d.toInt } def test(n1: Int): Unit = { println("ok") } test(10.1) } } class DB {} class MySQL { def sayOk(): Unit = { println("sayOk") } }
1) 當方法中的參數的類型與目標類型不一致時,或者是賦值時
implicit def f1(d: Double): Int = { d.toInt } def test1(n1: Int): Unit = { println("ok") } test1(9.1)
2) 當對象調用所在類中不存在的方法或成員時,編譯器會自動將對象進行隱式轉換(根據類型)
即編譯器是如何查找到缺失信息的,解析具備如下兩種規則:
1) 首先會在當前代碼做用域下查找隱式實體(隱式方法,隱式類,隱式對象)(通常是這種狀況)
2) 若是第一條規則查找隱式實體失敗,會繼續在隱式參數的類型的做用域裏查找。類型的做用域是指與該類型相關聯的所有伴生模塊,一個隱式實體的類型T它的查找範圍以下(第二種狀況範圍廣且複雜在使用時,應當儘可能避免出現)
a) 若是 T 被定義爲 T with A with B with C,那麼 A,B,C 都是 T 的部分,在 T 的隱式解析過程當中,它們的伴生對象都會被搜索
b) 若是 T 是參數化類型,那麼類型參數和與類型參數相關聯的部分都算做 T 的部分,好比 List[String]的隱式搜索會搜索 List 的伴生對象和 String 的伴生對象
c) 若是 T 是一個單例類型 p.T,即 T 是屬於某個 p 對象內,那麼這個 p 對象也會被搜索
d) 若是 T 是個類型注入 S#T,那麼 S 和 T 都會被搜索
1) 不能存在二義性
2) 隱式操做不能嵌套使用 //好比:隱式函數轉換
object boke_demo01 { def main(args: Array[String]): Unit = { //1. 隱式轉換不能有二義性 //2. 隱式轉換不能嵌套使用 implicit def f1(d: Double): Int = { d.toInt //val num2:Int = 2.3 //底層 f1$1(2.3) //f1$1對應的就是f1,就會造成遞歸 } val num1: Int = 1.1 } }