天天看點

Scala—— 12.隐式轉換

1.隐式轉換函數

隐式轉換函數是以implicit關鍵字聲明的帶有單個參數的函數,這種函數将會自動應用,将值從一種類型轉換為另一種類型。

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)
 
  }
}
           

隐式轉換函數的函數名可以是任意的,隐式函數與函數名無關,隻與函數簽名(函數參數類型和傳回值類型)有關。 隐式函數可以有多個(即:隐式函數清單),但是需要保證在目前環境下,隻有一個隐式函數能被識别。

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")
  }
}
           

2.隐式值

隐式值也叫隐式變量,将某個形參變量标記為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);
 
  }
}
           

當在程式中,同時有 隐式值、預設值、傳值,編譯器的優先級為 傳值 > 隐式值 > 預設值, 在隐式值比對時,不能有二義性。. 如果三個 (隐式值,預設值,傳值) 一個都沒有,就會報錯。

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
  }
}
           

3.隐式類

在Scala2.10後提供了隐式類,可以使用implicit聲明類,隐式類非常強大,同樣可以擴充類的功能,比前面使用隐式轉換豐富類庫功能更加的友善,在集合中隐式類會發揮重要作用。

隐式類所帶的構造參數有且隻能有一個。 隐式類必須被定義在“類”或“伴生對象”或“包對象”裡,即隐式類不能是頂級的(top-level objects)。隐式類不能是case class(樣例類)。作用域内不能有與之相同名稱的辨別符。

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")
  }
}
           

4.隐式的轉換時機

當方法中的參數的類型與目标類型不一緻時,或者是指派時。

當對象調用所在類中不存在的方法或成員時,編譯器會自動将對象進行隐式轉換(根據類型)。

5.隐式解析機制

首先會在目前代碼作用域下查找隐式實體(隐式方法,隐式類,隐式對象)(一般是這種情況)。

如果第一條規則查找隐式實體失敗,會繼續在隐式參數的類型的作用域裡查找。類型的作用域是指與該類型相關聯的全部伴生子產品,一個隐式實體的類型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 都會被搜尋。

隐式操作不能存在二義性。

隐式操作不能嵌套使用 //比如:隐式函數轉換。

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
  }
}
           

————Blueicex 2020/2/18 20:46 [email protected]