天天看點

ScalaNote14-隐式轉換

Intro

  在實際工作中,可能需要指定某些資料類型的互相轉化,這時候會用到隐式轉換和隐式函數。看一個Demo:

class test(){
    implicit def f1(d: Double): Int = {
        d.toInt
      }
    var x:Int=3.9
}
val testObj = new test
testObj.x      
warning: there was one feature warning; re-run with -feature for details
defined class test
testObj: test = test@799609b4
res22: Int = 3      

此時test類中的屬性x被強制從Double轉換成Int

隐式轉換的細節

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

隐式轉換的作用

  如果僅僅用來做資料轉換,似乎還不大材小用。如果需要為一個類增加一個方法,也可通過隐式轉換來實作(動态增加功能)。比如想為MySQL類增加一個delete方法。這個有點像動态混入trait,不用修改類的源代碼,直接增加方法。看個Demo:

class MySQL{
  def insert(): Unit = {
    println("MySQL:insert...")
  }
}
class DB {
  def delete1(): Unit = {
    println("DB:delete1...")
  }
}
implicit def addDelete(mysql:MySQL): DB = {
      new DB //這裡好像有點類似繼承了DB一樣。。。
}
trait addDelete2{
    def delete2(): Unit = {
    println("trait:delete2...")
  }
}
val mysql = new MySQL with addDelete2
mysql.insert()
mysql.delete1() 
mysql.delete2()      
MySQL:insert...
DB:delete1...
trait:delete2...





warning: there was one feature warning; re-run with -feature for details
defined class MySQL
defined class DB
addDelete: (mysql: MySQL)DB
defined trait addDelete2
mysql: MySQL with addDelete2 = $anon$1@13da528e      

如上所示,特質和隐私函數似乎都給MySQL類增加了delete方法,這倆有啥差别嗎?我也母雞。。。

隐式值

  隐式值也叫隐式變量,将某個形參變量标記為implicit,是以編譯器會在方法省略隐式參數的情況下去搜尋作用域内的隐式值作為預設參數。還是看個Demo:

// 隐式變量
implicit val str1: String = "jack"
//隐式參數
def hello(implicit name: String): Unit = {
println(name + " hello")
}
def sayHi(implicit name: String ="wade")
: Unit = {
println(name + " hi")
}
hello //注意:調用.不帶()
sayHi      
jack hello
jack hi





str1: String = jack
hello: (implicit name: String)Unit
sayHi: (implicit name: String)Unit      

細節

  • 隐式變量不能有重複的,比如兩個隐式變量都是指定String的值
  • 當時同時有隐式參數預設值和隐式變量時,隐式變量優先級更高,見sayHi
  • 如果有隐式參數但是沒有對應的隐式變量,則報錯

隐式類

之前我們通過隐式函數,給mysql增加了delete方法,隐式類也可以,看個demo:

class MySQL1 {
  def sayOk(): Unit = {
    println("sayOk")
  }
}
//DB1會對應生成隐式類
implicit class DB1(val m: MySQL1) {
   def addSuffix(): Unit = {
        println( "DB1addSuffix ... ")
   }
}
val mysql1 = new MySQL1
mysql1.sayOk()
// 此時mysql1有addSuffix方法  
mysql1.addSuffix()      
sayOk
DB1addSuffix ... 





defined class MySQL1
defined class DB1
mysql1: MySQL1 = MySQL1@2b2b690d      

隐式類的細節

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

隐式轉換其他細節

隐式準換的時機

  • 當方法中的參數的類型與目标類型不一緻時,比如前面資料類型轉換
  • 當對象調用所在類中不存在的方法或成員時,編譯器會自動将對象進行隐式轉換(根據類型)
  • 不能存在二義性,比如功能一樣的隐式函數
  • 隐式操作不能嵌套使用,比如在隐式函數toInt内部做指派操作​

    ​var x:Int=3.9​

繼續閱讀