隱式轉換與隱式參數
Scala 的隱式轉換,其實最核心的就是定義隱式轉換函數,即 implicit conversion function 。
定義的隱式轉換函數,只要在編寫的程序內引入,就會被 Scala 自動使用。
Scala 會根據隱式轉換函數的簽名,在程序中使用到隱式轉換函數接收的參數類型定義的對象時,會自動將其傳入隱式轉換函數,轉換爲另外一種類型的對象並返回。這就是“隱式轉換”。
隱式轉換函數叫什麼名字是無所謂的,因爲通常不會由用戶手動調用,而是由 Scala 進行調用。但是如果要使用隱式轉換,則需要對隱式轉換函數進行導入。因此通常建議將隱式轉換函數的名稱命名爲 “one2one” 的形式。
隱式轉換
要實現隱式轉換,只要程序可見的範圍內定義隱式轉換函數即可。Scala 會自動使用隱式轉換函數。
隱式轉換函數與普通函數唯一的語法區別就是,要以 implicit 開頭,而且最好要定義函數返回類型。
// 特殊售票窗口(只接受特殊人羣,比如學生、老人等)
class SpecialPerson(val name:String)
class Student(val name:String)
class Older(val name:String)
implicit def object2SpecialPerson(obj:Object):SpecialPerson = {
if(obj.getClass == classOf[Student]){
val stu = obj.asInstanceOf[Student];
new SpecialPerson(stu.name)
}
else if(obj.getClass == classOf[Older]){
val older = obj.asInstanceOf[Older];
new SpecialPerson(older.name)
}
else Nil
var ticketNumber = 0
def buySpecialTicket(p:SpecialPerson) = {
ticketNumber += 1
"T - "+ ticketNumber
}
使用隱式轉換加強現有類型
隱式轉換非常強大的一個功能,就是可以在不知不覺中加強現有類型的功能。也就是說,可以爲某個類定義一個加強版的類,並定義互相之間的隱式轉換,從而讓源類在使用加強版的方法時,由Scala自動進行隱式轉換爲加強類,然後再調用該方法。
class Man(val name: String)
class Superman(val name: String) {
def emitLaser = println("emit a laster!")
}
implicit def man2superman(man: Man): Superman = new Superman(man.name)
val leo = new Man("Li")
Li.emitLaser
隱式轉換函數作用域與導入
Scala 默認會使用兩種隱式轉換,一種是源類型,或者目標類型的伴生對象內的隱式轉換函數;一種是當前程序作用域內的可以用唯一標識符表示的隱式轉換函數。
如果隱式轉換函數不在上述兩種情況下的話,那麼就必須手動使用 import 語法引入某個包下的隱式轉換函數,比如 import test._ 。
通常建議,僅僅在需要進行隱式轉換的地方,比如某個函數或者方法內,用import 導入隱式轉換函數,這樣可以縮小隱式轉換函數的作用域,避免不需要的隱式轉換。
隱式轉換的發生時機
- 調用某個函數,但是給函數傳入的參數的類型,與函數定義的接收參數類型不匹配。
- 使用某個類型的對象,調用某個方法,而這個方法並不存在於該類型時。
- 使用某個類型的對象,調用某個方法,雖然該類型有這個方法,但是給方法傳入的參數類型,與方法定義的接收參數的類型不匹配。
隱式參數
所謂的隱式參數,指的是在函數或者方法中,定義一個用 implicit 修飾的參數,此時 Scala 會嘗試找到一個指定類型的,用 implicit 修飾的對象,即隱式值,並注入參數。
Scala 會在兩個範圍內查找:一種是當前作用域內可見的 val 或 var 定義的隱式變量;一種是隱式參數類型的伴生對象內的隱式值。
class SignPen{
def write(content:String) = println(content)
}
implicit val signPen = new SignPen
def signForExam(name:String)(implicit signPen:SignPen){
signPen.write(name + " come to exam in time.")
}