1. 程式人生 > >Scala入門第十篇--隱式轉換和隱式引數

Scala入門第十篇--隱式轉換和隱式引數

這次我們介紹隱式轉換和隱式引數,會做簡單的程式碼示例 對於概念看不懂的童鞋,可以先去看看程式碼的示例

隱式轉換

概念引入

在講述之前我們先引入一個例子

scala> 1 to 10
res9: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> 1.to(10)
res10: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

這兩種方式都可以實現生成1-10範圍的一個數組,而底層到底是如何實現的呢?

我們翻看了Scala中Int類的原始碼,並沒有找到to方法,而在RichInt中找到了這個方法,這裡其實就是在底層自動轉換為RichInt,簡單的說這就是Scala中的隱士轉換。

官方的話

  • Scala提供的隱式轉換和隱式引數功能,是非常有特色的功能。是Java等程式語言所沒有的功能。它可以允許你手動指定,將某種型別的物件轉換成其他型別的物件或者是給一個類增加方法。通過這些功能,可以實現非常強大、特殊的功能。
  • Scala的隱式轉換,其實最核心的就是定義隱式轉換方法,即implicit conversion function。定義的隱式轉換方法,只要在編寫的程式內引入,就會被Scala自動使用。Scala會根據隱式轉換方法的簽名,在程式中使用到隱式轉換方法接收的引數型別定義的物件時,會自動將其傳入隱式轉換方法,轉換為另外一種型別的物件並返回。這就是“隱式轉換”。其中所有的隱式值和隱式方法必須放到object中。
  • 然而使用Scala的隱式轉換是有一定的限制的,總結如下:
    • implicit關鍵字只能用來修飾方法、變數(引數)。
    • 隱式轉換的方法在當前範圍內才有效。如果隱式轉換不在當前範圍內定義(比如定義在另一個類中或包含在某個物件中),那麼必須通過import語句將其導。

隱式引數

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

  • 當前作用域內可見的val或var定義的隱式變數;
  • 一種是隱式引數型別的伴生物件內的隱式值;

隱式轉換方法作用域與匯入

  1. Scala預設會使用兩種隱式轉換,一種是源型別,或者目標型別的伴生物件內的隱式轉換方法;一種是當前程式作用域內的可以用唯一識別符號表示的隱式轉換方法。
  2. 如果隱式轉換方法不在上述兩種情況下的話,那麼就必須手動使用import語法引入某個包下的隱式轉換方法,比如import test._。通常建議,僅僅在需要進行隱式轉換的地方,用import匯入隱式轉換方法,這樣可以縮小隱式轉換方法的作用域,避免不需要的隱式轉換。

隱式轉換的時機

  • 當物件呼叫類中不存在的方法或成員時,編譯器會自動將物件進行隱式轉換
  • 當方法中的引數的型別與目標型別不一致時

隱式轉換和隱式引數案例

隱式轉換案例一(讓File類具備RichFile類中的read方法)

import java.io.File
import scala.io.Source

object MyPredef{
  //定義隱式轉換方法
  implicit def file2RichFile(file: File)=new RichFile(file)
}
class RichFile(val f:File) {
  def read()=Source.fromFile(f).mkString
}
object RichFile{
  def main(args: Array[String]) {
    val f=new File("E://words.txt")
    //使用import匯入隱式轉換方法
    import MyPredef._
    //通過隱式轉換,讓File類具備了RichFile類中的方法
    val content=f.read()
    println(content)
  }
}

隱式轉換案例二(超人變身)

class Man(val name:String)
class SuperMan(val name: String) {
  def heat=print("超人打怪獸")
}
object SuperMan{
  //隱式轉換方法
  implicit def man2SuperMan(man:Man)=new SuperMan(man.name)
  def main(args: Array[String]) {
      val hero=new Man("hero")
      //Man具備了SuperMan的方法
      hero.heat
  }
}

隱式轉換案例三(一個類隱式轉換成具有相同方法的多個類)

class A(c:C) {
    def readBook(): Unit ={
      println("A說:好書好書...")
    }
}
class B(c:C){
  def readBook(): Unit ={
    println("B說:看不懂...")
  }
  def writeBook(): Unit ={
    println("B說:不會寫...")
  }
}
class C
object AB{
  //建立一個類的2個類的隱式轉換
  implicit def C2A(c:C)=new A(c)
  implicit def C2B(c:C)=new B(c)
}
object B{
  def main(args: Array[String]) {
    //導包
    //1. import AB._ 會將AB類下的所有隱式轉換導進來
    //2. import AB._C2A 只匯入C類到A類的的隱式轉換方法
    //3. import AB._C2B 只匯入C類到B類的的隱式轉換方法
    import AB._
    val c=new C
    //由於A類與B類中都有readBook(),只能匯入其中一個,否則呼叫共同方法時程式碼報錯
    //c.readBook()
    //C類可以執行B類中的writeBook()
    c.writeBook()
  }
}

隱式引數案例四(員工領取薪水)

object Company{
  //在object中定義隱式值    注意:同一型別的隱式值只允許出現一次,否則會報錯
  implicit  val aaa="zhangsan"
  implicit  val bbb=10000.00
}
class Boss {
  //注意引數匹配的型別   它需要的是String型別的隱式值
  def callName()(implicit name:String):String={
    name+" is coming !"
  }
  //定義一個用implicit修飾的引數
  //注意引數匹配的型別    它需要的是Double型別的隱式值
  def getMoney()(implicit money:Double):String={
    " 當月薪水:"+money
  }
}
object Boss extends App{
  //使用import匯入定義好的隱式值,注意:必須先載入否則會報錯
  import Company._
  val boss =new Boss
  println(boss.callName()+boss.getMoney())

}

本次就介紹到這裡