1. 程式人生 > >Scala:隱式轉換與隱式引數

Scala:隱式轉換與隱式引數

Scala的隱式轉換,其實最核心的就是定義隱式轉換函式,即implicit conversion function。定義的隱式轉換函式,只要在編寫的程式內引入,就會被Scala自動使用。Scala會根據隱式轉換函式的簽名,在程式中使用到隱式轉換函式接收的引數型別定義的物件時,會自動將其傳入隱式轉換函式,轉換為另外一種型別的物件並返回。這就是“隱式轉換”。

隱式轉換

要實現隱式轉換,只要程式可見的範圍內定義隱式轉換函式即可。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("leo")
leo.emitLaser

隱式轉換函式作用域與匯入

Scala預設會使用兩種隱式轉換,一種是源型別,或者目標型別的伴生物件內的隱式轉換函式;一種是當前程式作用域內的可以用唯一識別符號表示的隱式轉換函式。

如果隱式轉換函式不在上述兩種情況下的話,那麼就必須手動使用import語法引入某個包下的隱式轉換函式,比如import test._。通常建議,僅僅在需要進行隱式轉換的地方,比如某個函式或者方法內,用iimport匯入隱式轉換函式,這樣可以縮小隱式轉換函式的作用域,避免不需要的隱式轉換。

隱式轉換的發生時機

  1. 呼叫某個函式,但是給函式傳入的引數的型別,與函式定義的接收引數型別不匹配
  2. 使用某個型別的物件,呼叫某個方法,而這個方法並不存在於該型別時
  3. 使用某個型別的物件,呼叫某個方法,雖然該型別有這個方法,但是給方法傳入的引數型別,與方法定義的接收引數的型別不匹配

隱式引數

所謂的隱式引數,指的是在函式或者方法中,定義一個用implicit修飾的引數,此時Scala會嘗試找到一個指定型別的,用implicit修飾的物件,即隱式值,並注入引數。

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

Scala會在兩個範圍內查詢:

  1. 當前作用域內可見的val或var定義的隱式變數
  2. 隱式引數型別的伴生物件內的隱式值