1. 程式人生 > >Scala入門到精通——第十九節 隱式轉換與隱式引數(二)

Scala入門到精通——第十九節 隱式轉換與隱式引數(二)

本節主要內容

  1. 隱式引數中的隱式轉換
  2. 函式中隱式引數使用概要
  3. 隱式轉換問題梳理

1. 隱式引數中的隱式轉換

前一講中,我們提到函式中如果存在隱式引數,在使用該函式的時候如果不給定對應的引數,則編譯器會自動幫我們搜尋相應的隱式值,並將該隱式值作為函式的引數,這裡面其實沒有涉及到隱式轉換,本節將演示如何利用隱式引數進行隱式轉換,下面的程式碼給定的是一個普通的比較函式:

object ImplicitParameter extends App {
//下面的程式碼不能編譯通過
//這裡面泛型T沒有具體指定,它不能直接使用
//<符號進行比較
  def compare[T](first:T,second:T)={
    if
(first < second) first else second } }

上面的程式碼要想使其編譯通過,可以前型別變數界定和檢視界定指定其上界為Ordered[T],例如:

object ImplicitParameter extends App {
  //指定T的上界為Ordered[T],所有混入了特質Ordered
  //的類都可以直接的使用<比較符號進行比較
  def compare[T<:Ordered[T]](first:T,second:T)={
    if (first < second) 
      first 
    else
second } }

這是一種解決方案,我們還有一種解決方案就是通過隱式引數的隱式轉換來實現,程式碼如下:

object ImplicitParameter extends App {
//下面程式碼中的(implicit order:T=>Ordered[T])
//給函式compare指定了一個隱式引數
//該隱式引數是一個隱式轉換
  def compare[T](first:T,second:T)(implicit order:T=>Ordered[T])={
    if (first > second) 
      first 
    else
second } println(compare("A","B")) }

2. 函式中隱式引數使用概要

要點1:在定義函式時,如果函式沒有柯里化,implicit關鍵字會作用於所有引數,例如:

//implicit關鍵字在下面的函式中只能出現一次
//它作用於兩個引數x,y,也即x,y都是隱式引數
def sum(implicit x: Int, y: Int) = x + y
//下面的函式不合法,函式如果沒有柯里化,不能期望
//implicit關鍵字會作用於其中一個引數
//def sum(x: Int, implicit y: Int) = x + y
//def sum(implicit x: Int, implicit y: Int) = x + y

另外,值得注意的是,def maxFunc(implicit x: Int, y: Int) = x + y 在使用時,也只能指定一個隱式值,即指定的隱式值分別會對應函式中的引數(這裡是x,y),程式碼如下:

def sum(implicit x: Int, y: Int) = x + y
//只能指定一個隱式值
//例如下面下定義的x會自動對應maxFunc中的
//引數x,y即x=3,y=3,從而得到的結果是6
 implicit val x:Int=3
//不能定義兩個隱式值
//implicit val y:Int=4
  println(sum)

要點2:要想使用implicit只作用於某個函式引數,則需要將函式進行柯里化,如:

def sum(x: Int)(implicit y:Int)=x+y

值得注意的是,下面這種兩種帶隱式引數的函式也是不合法的

def sum(x: Int)(implicit y:Int)(d:Int)=x+y+d
def sum(x: Int)(implicit y:Int)(implicit d:Int)=x+y+d

要點3: 匿名函式不能使用隱式引數,例如:

 val sum2=(implicit x:Int)=>x+1

要點4: 如何函式帶有隱式引數,則不能使用其偏函式,例如:


 def sum(x: Int)(implicit y:Int)=x+y
 //不能定義sum的偏函式,因為它帶有隱式引數
 //could not find implicit value for 
//parameter y: Int
//not enough arguments for method sum:
// (implicit y: Int)Int. Unspecified value parameter y.
 def sum2=sum _

3. 隱式轉換問題梳理

1 多次隱式轉換問題
在上一講中我們提到,隱式轉換從源型別到目標型別不會多次進行,也即源型別到目標型別的轉換隻會進行一次

class RichFile(val file:File){
  def read=Source.fromFile(file).getLines().mkString
}

//RichFileAnother類,裡面定義了read2方法
class RichFileAnother(val file:RichFile){
  def read2=file.read
}

 //隱式轉換不會多次進行,下面的語句會報錯
  //不能期望會發生File到RichFile,然後RifchFile到
  //RichFileAnthoer的轉換
  val f=new File("file.log").read2
  println(f)

注意這裡指的是源型別到目標型別的轉換隻會進行一次,並不是說不存在多次隱式轉換,在一般的方法呼叫過程中可能會出現多次隱式轉換,例如:

class ClassA {
  override def toString() = "This is Class A"
}
class ClassB {
  override def toString() = "This is Class B"
}
class ClassC {
  override def toString() = "This is  ClassC"
  def printC(c: ClassC) = println(c)
}
class ClassD

object ImplicitWhole extends App {
  implicit def B2C(b: ClassB) = {
    println("B2C")
    new ClassC
  }
  implicit def D2C(d: ClassD) = {
    println("D2C")
    new ClassC
  }
  //下面的程式碼會進行兩次隱式轉換
  //因為ClassD中並沒有printC方法
  //因為它會隱式轉換為ClassC(這是第一次,D2C)
  //然後呼叫printC方法
  //但是printC方法只接受ClassC型別的引數
  //然而傳入的引數型別是ClassB
  //型別不匹配,從而又發生了一次隱式轉地換(這是第二次,B2C)
  //從而最終實現了方法的呼叫
  new ClassD().printC(new ClassB)
}

還有一種情況也會發生多次隱式轉換,如果給函式定義了隱式引數,在實際執行過程中可能會發生多次隱式轉換,程式碼如下:

object Main extends App {
  class PrintOps() {
    def print(implicit i: Int) = println(i);
  }

  implicit def str2PrintOps(s: String) = {
    println("str2PrintOps")
    new PrintOps
  }

  implicit def str2int(implicit s: String): Int = {
    println("str2int")
    Integer.parseInt(s)
  }

  implicit def getString = {
    println("getString")
    "123"
  }
  //下面的程式碼會發生三次隱式轉換
  //首先編譯器發現String型別是沒有print方法的
  //嘗試隱式轉換,利用str2PrintOps方法將String
  //轉換成PrintOps(第一次)
  //然後呼叫print方法,但print方法接受整型的隱式引數
  //此時編譯器會搜尋隱式值,但程式裡面沒有給定,此時
  //編譯器會嘗試呼叫 str2int方法進行隱式轉換,但該方法
  //又接受一個implicit String型別引數,編譯器又會嘗試
  //查詢一個對應的隱式值,此時又沒有,因此編譯器會嘗試呼叫
  //getString方法對應的字串(這是第二次隱式轉換,
  //獲取一個字串,從無到有的過程)
  //得到該字串後,再呼叫str2int方法將String型別字串
  //轉換成Int型別(這是第三次隱式轉換)
  "a".print
}

上面這個例子來源於:愛國者的部落格,感謝該作者的無私奉獻

2 要不要用隱式轉換的問題

從上述程式碼中可以看到,隱式轉換功能很強大,但同時也帶來了程式複雜性性問題,在一個程式中如果大量運用隱式轉換,特別是涉及到多次隱式轉換時,會使程式碼理解起來變得比較困難,那到底要不要用隱式轉換呢?下面給出我自己開發實踐中的部分總結,供大家參考:
1 即使你能輕鬆駕馭scala語言中的隱式轉換,能不用隱式轉換就儘量不用
2 如果一定要用,在涉及多次隱式轉換時,必須要說服自己這樣做的合理性
3 如果只是炫耀自己的scala語言能力,請大膽使用

新增公眾微訊號,可以瞭解更多最新Spark、Scala相關技術資訊
這裡寫圖片描述