Scala--隱式轉換和隱式參數

1、隱式轉換java

隱式轉換函數: 指的是以implicit關鍵字聲明的帶有單個參數的函數。這樣的函數將被自動應用,將值從一種類型轉換爲另外一種類型git

 

例子:
github

package com.test



/**
  * Created by Edward on 2018/2/7.
  */

class Fraction(val n : Int, val d : Int){
  def * (other:Fraction) =  Fraction(n*other.n,d*other.d) // 定義Fraction * Fraction
}

object Fraction{
  def apply(n : Int, d : Int)= new Fraction(n, d)
}

object ImplicitFunc {
  //聲明須要 implicit 關鍵字,只有一個參數,自動將參數的類型轉換爲函數實現的其餘類型
  implicit def int2Fraction(n: Int) = Fraction(n, 1)
}

object Chapter21 {

  def main(args: Array[String]) {

    import com.test.ImplicitFunc._ //須要 import 函數所在具體的位置

    val i = 3 * Fraction(3,2)  //Int類型不能* Fraction類型, Int類型自動轉換爲Fraction類型
    println(i.n,i.d)
  }

}

結果:app

(9,2)

 

2、利用隱式轉換豐富現有類庫的功能函數

package com.test

import java.io.File

import scala.io.Source


/**
  * Created by Edward on 2018/2/7.
  */



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

object Chapter21 {

  implicit def file2RichFile (from :File) = new RichFile(from) //隱式轉換將原來的File類型轉換到這個新的類型;新的類型有read方法。

  def main(args: Array[String]) {

    val contents = new File("file/wordcount.txt").read
    println(contents)

  }
}

結果:oop

hello    world
good    morning

 

3、引入隱式轉換this

Scala會考慮以下的隱式轉換函數:spa

1.位於源或目標類型的伴生對象中的隱式函數scala

2.位於當前做用域能夠以單個標識符指代的隱式函數code

 

object Fraction{
  def apply(n : Int, d : Int)= new Fraction(n, d)
  implicit def int2Fraction(n: Int) = Fraction(n, 1) //隱式轉換在伴生對象中
}

使用import引入隱式函數  import com.test.Fraction._

 

 

4、隱式轉換規則

隱式轉換在如下三種狀況下發生:

一、當表達式的類型與預期的類型不一樣時

  sqrt(Fraction(1,4))  

    sqrt預期是Double, 將調用fraction2Double

二、當對象訪問一個不存在的成員時

  new File("README").read

    File沒有read方法,將調用file2RichFile

三、當對象調用某個方法,而該方法的參數聲明與傳入參數不匹配時

  3 * Fraction(4,5)

    Int的*方法不接受Fraction做爲參數,將調用int2Fraction

 

不會發生隱式轉換的狀況:

一、若是代碼在不使用隱式轉換的前提下經過編譯,則不會使用隱式轉換

  如: a * b 可以編譯,那麼編譯器不會嘗試 a * convert(b) 或 convert(a) * b;

二、編譯器不會同時執行多個轉換 

  如: convert(convert(a))*b;

三、存在二義性的轉換是個錯誤

  如:convert1(a) * b 和 convert2(a) * b;

package com.test

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


/**
  * Created by Edward on 2018/2/7.
  */

class Fraction(val n : Int, val d : Int){
  def * (other:Fraction) =  Fraction(n*other.n,d*other.d) // 定義Fraction * Fraction
}

object Fraction{
  def apply(n : Int, d : Int)= new Fraction(n, d)
  implicit def int2Fraction(n: Int) = Fraction(n, 1)
  implicit def fraction2Double(f:Fraction) = f.n * 1.0 / f.d
}

object Chapter21 {

  def main(args: Array[String]) {

    import com.chinahadoop.test.Fraction._  //須要 import 隱式函數所在具體的位置

    val i = Fraction(3,2) * 3   //轉換 Int 爲 Fraction 調用 int2Fraction
    println(i)
    val i1 = 3 * Fraction(3,2) //轉換 Fraction 爲 Int 調用 fraction2Double
    println(i1)
    
  }
}

結果:

com.test.Fraction@6879c0f4
4.5

兩個類型,優先轉換方法*後面的類型

若是註釋掉 以下代碼,則須要轉換*前面的類型

//implicit def int2Fraction(n: Int) = Fraction(n, 1)

結果爲:

4.5
4.5

這裏不存在二義性,這裏隱式轉換是針對不一樣的類型,存在二義性是針對相同的類型有不一樣的轉換方法。

 

5、隱式參數

object Chapter21 {

  case class Delimiters(left : String, right : String)
  implicit val quoteDelimiter = Delimiters("<<",">>")

  def main(args: Array[String]) {

    def quote(what : String)(implicit delimiter : Delimiters) = delimiter.left + what + delimiter.right

    println(quote("Hello"))

  }
}

輸出:

<<Hello>>

對於給定的數據類型,只有一個隱式的值

def quota(what: String)(implicit left : String, right : String) // 不正確 

 

6、利用隱式參數進行隱式轉換

def smaller[T](a : T, b : T) = if (a < b) a else b

這種實現方式是不對的,泛型T中不肯定 < 方法。

使用 T到Ordered[T]的隱式轉換, Ordered[T] 有接收參數爲T的<操做符。

def smaller[T](a : T, b : T)(implicit order:T => Ordered[T]) = if (order(a) < b) a else b
println(smaller(10,34))

理解 

implicit order:T => Ordered[T]

Ordered對象提供了從T到Ordered[T]的隱式轉換(隱式參數爲Ordering[T])

源碼:https://github.com/scala/scala/blob/v2.12.4/src/library/scala/math/Ordered.scala#L1

object Ordered {
  /** Lens from `Ordering[T]` to `Ordered[T]` */
  implicit def orderingToOrdered[T](x: T)(implicit ord: Ordering[T]): Ordered[T] =
    new Ordered[T] { def compare(that: T): Int = ord.compare(x, that) }
}

若是使用自定義類型,咱們能夠實現Ordered[Fraction]特質的compare方法。

class Fraction (val n : Int, val d : Int) extends Ordered[Fraction]{
  def * (other:Fraction) =  Fraction(n*other.n,d*other.d) // 定義Fraction * Fraction
  def compare(that:Fraction) = this.n - that.n
}

def smaller[T](a : T, b : T)(implicit order:T => Ordered[T]) = if (a < b) a else b

println(smaller(Fraction(3,6), Fraction(4,9)))

order不只是一個隱式參數,仍是一個隱式轉換。

 

7、上下文界定

 

 

 

Ordered 、Ordering

Scala提供兩個特質(trait)Ordered與Ordering用於比較。其中,Ordered混入(mix)Java的Comparable接口,而Ordering則混入Comparator接口。
在Java中:
實現Comparable接口的類,其對象具備了可比較性;
實現Comparator接口的類,則提供一個外部比較器,用於比較兩個對象。

Ordered與Ordering的區別與之相相似:Ordered特質定義了相同類型間的比較方式,但這種內部比較方式是單一的;Ordering則是提供比較器模板,能夠自定義多種比較方式。

相關文章
相關標籤/搜索