Scala入门到精通——第十八节 隐式转换与隐式参数(一)
本節主要內容
1. 隱式轉換簡介
在Scala語言當中,隱式轉換是一項強大的程序語言功能,它不僅能夠簡化程序設計,也能夠使程序具有很強的靈活性。要想更進一步地掌握scala語言,了解其隱式轉換的作用與原理是很有必要的,否則很難得以應手地處理日常開發中的問題。
在scala語言中,隱式轉換是無處不在的,只不過scala語言為我們隱藏了相應的細節,例如scala中的類繼承層次結構中:
它們存在固有的隱式轉換,不需要人工進行干預,例如Float在必要情況下自動轉換為Double類型
在前一講的視圖界定中我們也提到,視圖界定可以跨越類層次結構進行,它背后的實現原理就是隱式轉換,例如Int類型會視圖界定中會自動轉換成RichInt,而RichInt實現了Comparable接口,當然這里面的隱式轉換也是scala語言為我們設計好的
本節將對隱式轉換中的隱式轉換函數、隱式轉換規則、隱式參數進行介紹,使大家明白如何自己實現隱式轉換操作。
2. 隱式轉換函數
下列賦值如果沒有隱式轉換的話會報錯:
scala> val x:Int=3.5 <console>:7: error: type mismatch;found : Double(3.5)required: Intval x:Int=3.5^添加隱式轉換函數后可以實現Double類型到Int類型的賦值
//定義了一個隱式函數double2Int,將輸入的參數 //從Double類型轉換到Int類型 scala> implicit def double2Int(x:Double)=x.toInt warning: there were 1 feature warning(s); re-run with -feature for details double2Int: (x: Double)Int //定義完隱式轉換后,便可以直接將Double類型賦值給Int類型 scala> val x:Int=3.5 x: Int = 3- 1
隱式函數的名稱對結構沒有影響,即implicit def double2Int(x:Double)=x.toInt函數可以是任何名字,只不能采用source2Target這種方式函數的意思比較明確,閱讀代碼的人可以見名知義,增加代碼的可讀性。
隱式轉換功能十分強大,可以快速地擴展現有類庫的功能,例如下面的代碼:
package cn.scala.xtwyimport java.io.File import scala.io.Source //RichFile類中定義了Read方法 class RichFile(val file:File){def read=Source.fromFile(file).getLines().mkString }object ImplicitFunction extends App{implicit def double2Int(x:Double)=x.toIntvar x:Int=3.5//隱式函數將java.io.File隱式轉換為RichFile類implicit def file2RichFile(file:File)=new RichFile(file)val f=new File("file.log").readprintln(f) }3. 隱式轉換規則
隱式轉換可以定義在目標文件當中,例如
implicit def double2Int(x:Double)=x.toInt var x:Int=3.5隱式轉換函數與目標代碼在同一個文件當中,也可以將隱式轉換集中放置在某個包中,在使用進直接將該包引入即可,例如:
package cn.scala.xtwy import java.io.File import scala.io.Source//在cn.scala.xtwy包中定義了子包implicitConversion //然后在object ImplicitConversion中定義所有的引式轉換方法 package implicitConversion{object ImplicitConversion{implicit def double2Int(x:Double)=x.toIntimplicit def file2RichFile(file:File)=new RichFile(file)}}class RichFile(val file:File){def read=Source.fromFile(file).getLines().mkString }object ImplicitFunction extends App{//在使用時引入所有的隱式方法import cn.scala.xtwy.implicitConversion.ImplicitConversion._var x:Int=3.5val f=new File("file.log").readprintln(f) }這種方式在scala語言中比較常見,在前面我們也提到,scala會默認幫我們引用Predef對象中所有的方法,Predef中定義了很多隱式轉換函數,下面是Predef的部分隱式轉換源碼:
scala> :implicits -v /* 78 implicit members imported from scala.Predef *//* 48 inherited from scala.Predef */implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A]implicit def any2Ensuring[A](x: A): Ensuring[A]implicit def any2stringadd(x: Any): runtime.StringAddimplicit def any2stringfmt(x: Any): runtime.StringFormatimplicit def boolean2BooleanConflict(x: Boolean): Objectimplicit def byte2ByteConflict(x: Byte): Objectimplicit def char2CharacterConflict(x: Char): Objectimplicit def double2DoubleConflict(x: Double): Objectimplicit def float2FloatConflict(x: Float): Objectimplicit def int2IntegerConflict(x: Int): Objectimplicit def long2LongConflict(x: Long): Objectimplicit def short2ShortConflict(x: Short): Object//....................那什么時候會發生隱式轉換呢?主要有以下幾種情況:
1 當方法中參數的類型與實際類型不一致時,例如
2 當調用類中不存在的方法或成員時,會自動將對象進行隱式轉換,例如:
package cn.scala.xtwyimport java.io.File import scala.io.Source //RichFile類中定義了Read方法 class RichFile(val file:File){def read=Source.fromFile(file).getLines().mkString }object ImplicitFunction extends App{implicit def double2Int(x:Double)=x.toIntvar x:Int=3.5//隱式函數將java.io.File隱式轉換為RichFile類implicit def file2RichFile(file:File)=new RichFile(file)//File類的對象并不存在read方法,此時便會發生隱式轉換//將File類轉換成RichFileval f=new File("file.log").readprintln(f) }- 1
前面我們講了什么情況下會發生隱式轉換,下面我們講一下什么時候不會發生隱式轉換:
1 編譯器可以不在隱式轉換的編譯通過,則不進行隱式轉換,例如
//這里定義了隱式轉換函數 scala> implicit def double2Int(x:Double)=x.toInt warning: there were 1 feature warning(s); re-run with -feature for details double2Int: (x: Double)Int//下面幾條語句,不需要自己定義隱式轉換編譯就可以通過 //因此它不會發生前面定義的隱式轉換 scala> 3.0*2 res0: Double = 6.0scala> 2*3.0 res1: Double = 6.0scala> 2*3.7 res2: Double = 7.42 如果轉換存在二義性,則不會發生隱式轉換,例如
package implicitConversion{object ImplicitConversion{implicit def double2Int(x:Double)=x.toInt//這里定義了一個隱式轉換implicit def file2RichFile(file:File)=new RichFile(file)//這里又定義了一個隱式轉換,目的與前面那個相同implicit def file2RichFile2(file:File)=new RichFile(file)}}class RichFile(val file:File){def read=Source.fromFile(file).getLines().mkString }object ImplicitFunction extends App{import cn.scala.xtwy.implicitConversion.ImplicitConversion._var x:Int=3.5//下面這條語句在編譯時會出錯,提示信息如下://type mismatch; found : java.io.File required:// ?{def read: ?} Note that implicit conversions //are not applicable because they are ambiguous: //both method file2RichFile in object //ImplicitConversion of type (file: //java.io.File)cn.scala.xtwy.RichFile and method //file2RichFile2 in object ImplicitConversion of //type (file: java.io.File)cn.scala.xtwy.RichFile //are possible conversion functions from java.io.File to ?{def read: ?} value read is not a member of java.io.Fileval f=new File("file.log").readprintln(f) }編譯提示隱式轉換存在二義性(ambiguous)
3 隱式轉換不會嵌套進行,例如
package cn.scala.xtwy import java.io.File import scala.io.Sourcepackage implicitConversion{object ImplicitConversion{implicit def double2Int(x:Double)=x.toIntimplicit def file2RichFile(file:File)=new RichFile(file)//implicit def file2RichFile2(file:File)=new RichFile(file)implicit def richFile2RichFileAnother(file:RichFile)=new RichFileAnother(file)}}class RichFile(val file:File){def read=Source.fromFile(file).getLines().mkString }//RichFileAnother類,里面定義了read2方法 class RichFileAnother(val file:RichFile){def read2=file.read }object ImplicitFunction extends App{import cn.scala.xtwy.implicitConversion.ImplicitConversion._var x:Int=3.5//隱式轉換不會多次進行,下面的語句會報錯//不能期望會發生File到RichFile,然后RifchFile到//RichFileAnthoer的轉換val f=new File("file.log").read2println(f) }理解了這些規則之后,在使用隱式轉換時才能夠得心應手
4. 隱式參數
在一般的函數據定義過程中,需要明確傳入函數的參數,代碼如下:
package cn.scala.xtwyclass Student(var name:String){//將Student類的信息格式化打印def formatStudent(outputFormat:OutputFormat)={outputFormat.first+" "+this.name+" "+outputFormat.second} }class OutputFormat(var first:String,val second:String)object ImplicitParameter {def main(args: Array[String]): Unit = {val outputFormat=new OutputFormat("<<",">>")println(new Student("john").formatStudent(outputFormat))} } //執行結果 //<< john >>如果給函數定義隱式參數的話,則在使用時可以不帶參數,代碼如下:
package cn.scala.xtwy class Student(var name:String){//利用柯里化函數的定義方式,將函數的參數利用//implicit關鍵字標識//這樣的話,在使用的時候可以不給出implicit對應的參數def formatStudent()(implicit outputFormat:OutputFormat)={outputFormat.first+" "+this.name+" "+outputFormat.second} }class OutputFormat(var first:String,val second:String)object ImplicitParameter {def main(args: Array[String]): Unit = {//程序中定義的變量outputFormat被稱隱式值implicit val outputFormat=new OutputFormat("<<",">>")//在.formatStudent()方法時,編譯器會查找類型//為OutputFormat的隱式值,本程序中定義的隱式值//為outputFormatprintln(new Student("john").formatStudent())} } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Scala入门到精通——第十八节 隐式转换与隐式参数(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Keras笔记(一)一些基本概念
- 下一篇: Scala入门到精通——第十九节 隐式转