一文搞懂scala隱式轉換


隱式轉換定義

隱式轉換是在Scala編譯器進行類型匹配時,如果找不到合適的類型,那么隱式轉換會讓編譯器在作用范圍內自動推導出來合適的類型。通過隱式轉換,程序員可以在編寫Scala程序時故意漏掉一些信息,讓編譯器去嘗試在編譯期間自動推導出這些信息來,這種特性可以極大的減少代碼量,忽略那些冗長,過於細節的代碼。

使用方式

1.將方法或變量標記為implicit

2.將方法的參數列表標記為implicit

3.將類標記為implicit

轉換形式

隱式值:用於給方法提供參數

隱式視圖:用於類型間轉換或使針對某類型的方法能調用成功

隱式類(2.10+支持):作為原有類的增強,在java中只能通過繼承來實現;scala中通過隱式類的方式,耦合程度低,且作用范圍粒度更細

詳細說明

隱式值

隱式值是指在定義參數時前面加上implicit,隱式函數是指在定義方法時,方法中的部分參數是由implicit修飾。

例1:聲明person方法。其參數為name,類型String

scala> def person(implicit name : String) = name   //name為隱式參數
person: (implicit name: String)String

直接調用person方法

scala> person
<console>:9: error: could not find implicit value for parameter name: String
              person
              ^

報錯!編譯器說無法為參數name找到一個隱式值

定義一個隱式值后再調用person方法

scala> implicit val p = "mobin"   //p被稱為隱式值
p: String = mobin
scala> person
res1: String = mobin

因為將p變量標記為implicit,所以編譯器會在方法省略隱式參數的情況下去搜索作用域內的隱式值作為缺少參數。

但是如果此時你又在REPL中定義一個隱式變量,再次調用方法時就會報錯

scala> implicit val p1 = "mobin1"
p1: String = mobin1
scala> person
<console>:11: error: ambiguous implicit values:
 both value p of type => String
 and value p1 of type => String
 match expected type String
              person
              ^

匹配失敗,所以隱式轉換必須滿足無歧義規則,在聲明隱式參數的類型是最好使用特別的或自定義的數據類型,不要使用Int,String這些常用類型,避免碰巧匹配

隱式視圖

隱式轉換為目標類型

把一種類型自動轉換到另一種類型

例2:將整數轉換成字符串類型:

scala> def foo(msg : String) = println(msg)
foo: (msg: String)Unit
 
scala> foo(10)
<console>:11: error: type mismatch;
found : Int(10)
required: String
foo(10)
^

顯然不能轉換成功,解決辦法就是定義一個轉換函數給編譯器將int自動轉換成String

scala> implicit def intToString(x : Int) = x.toString
intToString: (x: Int)String
 
scala> foo(10)
10

隱式轉換調用類中本不存在的方法

例3:通過隱式轉換,使對象能調用類中本不存在的方法

class SwingType{
  def  wantLearned(sw : String) = println("兔子已經學會了"+sw)
}
object swimming{
  implicit def learningType(s : AminalType) = new SwingType
}
class AminalType
object AminalType extends App {
  import com.mobin.scala.Scalaimplicit.swimming._
  val rabbit = new AminalType
    rabbit.wantLearned("breaststroke")         //蛙泳
}

編譯器在rabbit對象調用時發現對象上並沒有wantLearning方法,此時編譯器就會在作用域范圍內查找能使其編譯通過的隱式視圖,找到learningType方法后,編譯器通過隱式轉換將對象轉換成具有這個方法的對象,之后調用wantLearning方法。

可以將隱式轉換函數定義在伴生對象中,在使用時導入隱式視圖到作用域中即可(如例4的learningType函數)

還可以將隱式轉換函數定義在包對象中,同樣在使用時導入作用域即可,如例4

例4:

class SwingType{
  def  wantLearned(sw : String) = println("兔子已經學會了"+sw)
}

package swimmingPage{
  object swimming{
    implicit def learningType(s : AminalType) = new SwingType  //將轉換函數定義在包中
  }
}
class AminalType
object AminalType extends  App{
  import com.mobin.scala.Scalaimplicit.swimmingPage.swimming._  //使用時顯示的導入
  val rabbit = new AminalType
    rabbit.wantLearned("breaststroke")         //蛙泳
}

像intToString,learningType這類的方法就是隱式視圖,通常為Int => String的視圖,定義的格式如下:

implicit def originalToTarget ( : OriginalType) : TargetType

其通常用在於以兩種場合中:

  1. 如果表達式不符合編譯器要求的類型,編譯器就會在作用域范圍內查找能夠使之符合要求的隱式視圖。如例2,當要傳一個整數類型給要求是字符串類型參數的方法時,在作用域里就必須存在Int => String的隱式視圖。

  2. 給定一個選擇e.t,如果e的類型里並沒有成員t,則編譯器會查找能應用到e類型並且返回類型包含成員t的隱式視圖,如例3。

隱式類

在scala2.10后提供了隱式類,可以使用implicit聲明類,但是需要注意以下幾點:

1.其所帶的構造參數有且只能有一個

2.隱式類必須被定義在類,伴生對象和包對象里

3.隱式類不能是case class(case class在定義會自動生成伴生對象與2矛盾)

4.作用域內不能有與之相同名稱的標示符

例5:

[復制代碼](javascript:void(0)😉

object Stringutils {  implicit class StringImprovement(val s : String){   //隱式類      def increment = s.map(x => (x +1).toChar)  }}object  Main extends  App{  import com.mobin.scala.implicitPackage.Stringutils._  println("mobin".increment)}

[復制代碼](javascript:void(0)😉

編譯器在mobin對象調用increment時發現對象上並沒有increment方法,此時編譯器就會在作用域范圍內搜索隱式實體,發現有符合的隱式類可以用來轉換成帶有increment方法的StringImprovement類,最終調用increment方法。

隱式轉換的時機:

1.當方法中的參數的類型與目標類型不一致時

2.當對象調用類中不存在的方法或成員時,編譯器會自動將對象進行隱式轉換

隱式解析機制

即編譯器是如何查找到缺失信息的,解析具有以下兩種規則:

1.首先會在當前代碼作用域下查找隱式實體(隱式方法 隱式方法 隱式類 隱式對象)

2.如果第一條規則查找隱式實體失敗,會繼續在隱式參數的類型的作用域里查找

類型的作用域是指與該類型相關聯的全部伴生模塊,一個隱式實體的類型T它的查找范圍如下:

(1)如果T被定義為T with A with B with C,那么A,B,C都是T的部分,在T的隱式解析過程中,它們的伴生對象都會被搜索

(2)如果T是參數化類型,那么類型參數和與類型參數相關聯的部分都算作T的部分,比如List[String]的隱式搜索會搜索List的

伴生對象和String的伴生對象

(3) 如果T是一個單例類型p.T,即T是屬於某個p對象內,那么這個p對象也會被搜索

(4) 如果T是個類型注入S#T,那么S和T都會被搜索

隱式轉換的前提

1.不存在二義性(如例1),一般類型建議不參與隱式轉換,出場率高可能會導致計算出錯;

2.隱式操作不能嵌套使用(如 convert1(covert2(x)))+y

3.代碼能夠在不使用隱式轉換的前提下能編譯通過,就不會進行隱式轉換


免責聲明!

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



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