【Scala】什么是隱式轉換?它又能用來干嘛?該怎么用



定義

隱式參數

隱式參數指在函數或者方法中,定義一個用implicit修飾的參數,此時Scala會嘗試找到一個指定類型的,用implicit修飾的參數,即隱式值,並注入參數。
Scala會在兩個范圍進行查找:
1.當前作用域內可見的val或var定義的隱式變量
2.隱式參數類型的伴生對象內的隱式值
 

隱式轉換

當Scala編譯器進行類型匹配時,如果找不到合適的候選,那么隱式轉化提供了另外一種途徑來告訴編譯器如何將當前的類型轉換成預期類型。
其中最核心的就是自己定義隱式轉換方法(implicit conversion function),Scala會根據隱式轉換方法的簽名,在程序中使用到隱式轉換方法接收的參數類型定義的對象時,會自動將其傳入隱式轉換方法,轉換為另外一種類型的對象並返回。所有的隱式值和隱式方法都必須放到object中

什么時候要進行隱式轉換?
1.當對象調用類中不存早的方法或成員時,編譯器會自動將對象進行隱式轉換
2.當方法中的參數類型與目標類型不一致時

隱式轉換的限制:
1.implicit關鍵字只能用來修飾方法、變量(參數)
2.隱式轉換的方法在當前范圍內才有效,如果隱式轉換不在當前范圍內定義(比如定義在了另一個類中或者包含在某個對象中),就必須通過import語句導入

隱式轉換使用方式:
1.將方法或變量標記為implicit
2.將方法的參數列表標記為implicit
3.將類標記為implicit


隱式值:給方法提供參數

//先定義一個普通的方法
scala> def m1(name:String) = println(name)
m1: (name: String)Unit

//給定匹配的參數類型不會報錯
scala> m1("LeiJun")
LeiJun
//如果給了不匹配的參數類型就會報錯
scala> m1(2)
<console>:13: error: type mismatch;
 found   : Int(2)
 required: String
       m1(2)
          ^
//或者不給參數也會報錯
scala> m1
<console>:13: error: missing argument list for method m1
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `m1 _` or `m1(_)` instead of `m1`.
       m1
       ^
//現在重新定義一個方法,參數用implicit修飾
scala> def m2(implicit name:String) = println(name)
m2: (implicit name: String)Unit
//只輸出m2依然會報錯,不過和之前已經不一樣了
//顯示的是“找不到參數name: String的隱式值”
scala> m2
<console>:13: error: could not find implicit value for parameter name: String	
       m2
       ^
//那就定義隱式值
scala> implicit val a:String = "money"
a: String = money
//只輸出m2發現發生了隱式轉換
scala> m2
money

scala> m2("123")
123
//如果再定義一個同類型的隱式值
scala> implicit val b:String = "love"
b: String = love
//就會報錯,“愛情面包不可兼得”
scala> m2
<console>:15: error: ambiguous implicit values:
 both value a of type => String
 and value b of type => String
 match expected type String
       m2
       ^

隱式視圖

將Int和Double類型轉換為String

scala> def m1(name:String) = println(name)
m1: (name: String)Unit
//定義一個將Int類型轉換為String的隱式轉換方法
scala> implicit def Int2String(x:Int) = x.toString 
warning: there was one feature warning; re-run with -feature for details
Int2String: (x: Int)String

scala> m1(123)
123
//定義一個將Double類型轉換為String的隱式轉換方法
scala> implicit def Double2String(x:Double) = x.toString
warning: there was one feature warning; re-run with -feature for details
Double2String: (x: Double)String

scala> m1(3.14)
3.14

 

狗狗學技能(使用別的類中的方法)

狗狗學習導盲技能

class Learn {
  def learnMore(skills:String) = println(skills)
}

// implicit 隱式轉換
object Teaching{
  //定義隱式轉換的方法 讓一個類可以調用另一個類
  implicit def dogLearning(dog: Dog) = new Learn
}

class Dog{
}

object FinalDog{
  def main(args: Array[String]): Unit = {
    //創建Dog對象
    val dog = new Dog
    //手動導入隱式轉換方法
    import cn.itcast.scala.implicit_demo.Teaching.dogLearning
    //Dog自己的類中並沒有learnMore方法,通過隱式轉換就可以直接調用Learn類中的方法
    dog.learnMore("導盲")
  }
}

使用規則

1. Scala一般指考慮處於作用域之內的隱式轉換,不過可以通過import調用其他類中的方法,比如上面用到的import cn.itcast.scala.implicit_demo.Teaching.dogLearning

2. 無歧義規則。隱式轉換必須在不存在其他可插入轉換的前提下才能插入。比如:

scala> implicit def String2Int(s:String) = s.toInt
warning: there was one feature warning; re-run with -feature for details
String2Int: (s: String)Int

scala> implicit def String2Double(s:String) = s.toDouble
<console>:18: error: type mismatch;
 found   : s.type (with underlying type String)
 required: ?{def toDouble: ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method augmentString in object Predef of type (x: String)scala.collection.immutable.StringOps
 and method String2Int of type (s: String)Int
 are possible conversion functions from s.type to ?{def toDouble: ?}
       implicit def String2Double(s:String) = s.toDouble
                                              ^
<console>:18: error: value toDouble is not a member of String
 Note: implicit method String2Double is not applicable here because it comes after the application point and it lacks an explicit result type
       implicit def String2Double(s:String) = s.toDouble
                                                ^

上面兩個都想通過隱式轉換同時將String類型轉換為Int和Double,如果要進行轉換運算時,就會產生歧義,scala會不明白到底是要將輸入的String類型參數轉換為Int類型還是Double類型

3. 單一調用原則。scala只會嘗試一個隱式操作,不會對某個變量執行多次隱式轉換。

比如說,現在想將輸入的String類型參數轉換為Int,然后定義了一個將String類型隱式轉換為Int的方法implicit def String2Int(s:String) = s.toInt,結果又想將輸入的Int參數轉換為Double類型,又將Double類型轉換為String類型,編譯器將只會嘗試最初定義的隱式轉換方法

4. 顯示操作現行原則。編譯器發現代碼類型並沒有發生錯誤時,不會進行隱式轉換操作。

5.最好將自己定義的隱式轉換方法放到一個類中,使用時通過import,這樣方便管理,復用性也比較強


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM