隐式转换、隐式参数、上下文界定以及隐式类 学习笔记

来源:互联网 发布:淘宝退货率高会怎样 编辑:程序博客网 时间:2024/06/03 16:33

package myscala.scala_for_the_impatient.implicitsimport java.io.Fileimport scala.io.Source/** * 隐式转换和隐式参数是Scala的两个功能强大的工具,在幕后处理那种很有价值的东西。 * 要点: * 1)、隐式转换用于在类型之间做转换 * 2)、你必须引入隐式转换,并确保它们可以以单个标识符的形式出现在当前作用域。 * 3)、隐式参数列表会要求指定类型的对象。它们可以从当前作用域中以单个标识符定义的隐式 * 对象获取,或者从目标类型的伴生对象获取 * 4)、如果隐式参数是一个单参数的函数,那么它同时也会作为隐式转换使用。 * 5)、类型参数的上下文界定要求存在一个指定类型的隐式对象。 * 6)、如果有可能定位到一个隐式对象,这一点可以作为证据证明某个类型转换是合法的。 * * 1、隐身转换(隐式转换函数) * 所谓“隐式转换函数”指的是以“implicit”关键字声明的带有“单个”参数的函数。 * 隐式转换函数将被自动应用,将值(即单个参数的值)从一种类型转换为另一种类型。 * 你可以给隐式转换函数取任何名称。建议用source2Target这种约定俗成的名称。 * 一般情况下我们不显示地调用它,但有时候我们也需要显示地引入转换函数。 * * 2、利用隐式转换丰富现有类库的功能 * 如果希望某个类有某个方法,而这个类的作者却没有提供,那么可以使用隐式转换丰富 * 现有类库的功能。做法如下: * 步骤1:定义一个经过丰富的类型,提供你想要的功能 * 步骤2:再提供一个隐式转换函数将原来的类型转换到这个新的类型。 * 这样就可以在原来的对象上调用新增的方法了 * * 3、引入隐式转换 * Scala会考虑如下的隐式转换函数: * 1)、位于源或目标类型的伴生对象中的隐式函数 * 2)、位于当前作用域可以以单个标识符指代的隐式函数 * 比如我们可以将int2Fraction放入到Fraction的伴生对象中,这样就不用显示的import了。 * 假定我们有另一个double2Fraction放到了FractionConversions中,FractionConversions对象 * 位于myscala.scala_for_the_impatient.implicits这个包中。如果想使用这个转换,就需要引入 * FractionConversions对象,像这样: * import myscala.scala_for_the_impatient.implicits.FractionConversions._ * 仅仅是 * import myscala.scala_for_the_impatient.implicits.FractionConversions * 是不够的。 * 可以将引入局部化,以避免不必要的转换发生,如在方法中引入。 * * 4、隐式转换的规则 * 隐式转换会在如下三种情况时会被考虑: * 1)表达式的类型与预期的不同时 * 2)当对象访问一个不存在的成员时 * 3)当对象调用某个方法,而这个方法的参数类型与传入参数不匹配时 * */class Fraction(val fz: Int, val fm: Int) {  def *(that: Fraction) = {    Fraction(this.fz * that.fz, this.fm * that.fm)  }  override def toString: String = this.fz + "/" + this.fm}object Fraction {  //############# 例1 隐式转换函数 ########  //############# 例4 引入隐式转换 ########  // 将int2Fraction放入Fraction的伴生对象中  implicit def int2Fraction(n: Int) = new Fraction(n, 1)  //隐式转换  implicit def fraction2Ordered(fraction:Fraction) = new Ordered[Fraction] {    override def compare(that: Fraction): Int = {      if ((fraction.fz.toDouble / fraction.fm - that.fz.toDouble / that.fm) > 0) 1 else -1    }  }  /*  这边隐式转换和隐式值应该只要写一个就可以了  //隐式值。该隐式值是将一个Fraction转换成Ordered[Fraction]的函数  implicit val orderedFraction = (fraction:Fraction) => new Ordered[Fraction]{    override def compare(that: Fraction): Int = {      if((fraction.fz.toDouble / fraction.fm - that.fz.toDouble / that.fm) > 0) 1 else -1    }  }  */  def apply(fz: Int, fm: Int) = {    new Fraction(fz, fm)  }  //############# 例4 结束 #########################  //##################### 例1 结束 ##################}//############# 例5 引入隐式转换 ########object FractionConversions {  implicit def double2Fraction(n: Double) = Fraction(n.toInt, 1)  implicit def fraction2Double(f: Fraction) = f.fz.toDouble / f.fm}//############# 例5 结束 ########//#############例2 利用隐式转换丰富现有类库的功能###########//业务:丰富java.io.File类,使得它有个read方法来读取文件//步骤1:写一个经过丰富的类,提供read方法class RichFile(val from: File) {  def read = Source.fromFile(from.getPath).mkString}object RichFile {  //步骤2:再提供一个隐身转换函数,将File类型转化成RichFile类型  implicit def file2RichFile(from: File) = new RichFile(from)}//#################### 例2 结束 #########################//############# 例3 利用隐式转换丰富现有类库的功能 见Example1 ###########class NeedDouble(n: Double)object ImplictsMain {  def main(args: Array[String]) {    //测试例1、例4    val result = 2 * Fraction(2, 3)    println(result)    //测试例2    import myscala.scala_for_the_impatient.implicits.RichFile.file2RichFile    val contents = new File("D:\\words.txt").read    println(contents)    //############ 引入隐式转换的测试 开始#################    //必须引入下面这句,为了下面的测试,所以将下面两句注释掉    //import myscala.scala_for_the_impatient.implicits.FractionConversions._    //val result2: Fraction = 2.3 * Fraction(2,3)    //可以选择你想要的特定的转换。如果你想要的是一个fraction2Double,而不是double2Fraction,你可以直接引用它    //import myscala.scala_for_the_impatient.implicits.FractionConversions.fraction2Double    //val result3: Double = 2.3 * Fraction(2,3)    //如果某个特定的隐式转换给你带来麻烦,你也可以将它排除 //测试后发现有问题,无法排除    //import myscala.scala_for_the_impatient.implicits.FractionConversions.{fraction2Double => _,_}    //val result4: Double = 2.3 * Fraction(2,3)    //############ 引入隐式转换的测试 结束 #################    //############ 隐式转换规则 ############################    //表达式的类型与预期的类型不符时    import myscala.scala_for_the_impatient.implicits.FractionConversions._    println(new NeedDouble(Fraction(4, 2)))    //当对象访问一个不存在的成员时    import myscala.scala_for_the_impatient.implicits.RichFile._    new File("D:\\words.txt").read    //当对象调用某个方法,而这个方法的参数类型与传入参数不匹配时    3 * Fraction(4, 5)  }}

package myscala.scala_for_the_impatient.implicits/** * 5、隐式参数 * 函数或方法可以带有一个标记为implicit的参数列表。这种情况下,编译器会查找缺省值,提供给该 * 函数或方法。 * * 6、利用隐式参数进行隐式转换 * 隐式的函数参数也可以被用作隐式转换。 *///############ 例6 隐式参数 ###############case class Delimiters(left: String, right: String)object FrenchPunctuation {  implicit val quoteDelimiters = new Delimiters("<",">")  implicit val quoteLeft = "<"  // ...}object ImplicitsParams {  //这个方法是柯里化的,并且该方法带了一个隐式参数。当隐式参数缺省时,会去查找相同类型的带有implicit的值,作为这个值。  def quote(what: String)(implicit delims: Delimiters) = {    delims.left + what + delims.right  }  def quoteThree(what: String, delims: Delimiters = new Delimiters("<",">")) = {    delims.left + what + delims.right  }  //对于给定的数据类型,只能有一个隐式的值。因此使用常用类型的隐式参数并不是一个好主意。例如:/*  def quoteTwo(what: String)(implicit left: String,right: Right) = {  //别这样做!!    left + what + right  }*/  //上面的代码行不通,因为没法提供两个不同的字符串。  def main(args: Array[String]) {    //测试例6    //在quote方法中你可以显示地传入一个Delimiters对象    val q1 = quote("Hadoop")(new Delimiters("<", ">"))    println(q1) // <<Hadoop>>    //也可以省略隐式参数列表,直接写成quote("Hadoop"),这时编译器将会查找一个类型为Delimiters的隐式值。    //这必须是一个被声明为implicit的值。编译器将会在如下两个地方查找这样的一个对象:    //1、在当前作用域所有可以用单个标识符指代的满足类型要求的val和def    //2、与所有要求类型相关联的类型的伴生对象。相关联的类型包括所要求类型本身,以及它的类型参数    // (如果它是一个参数化的类型的话)    //引入所有值import FrenchPunctuation._,引入特定值import FrenchPunctuation.quoteDelimiters    import FrenchPunctuation._    println(quote("Spark"))    println(quote("Spark")(new Delimiters("@","!")))    println(quoteThree("Spark"))    println(quoteThree("Spark",new Delimiters("@","!")))  }}//############ 例6 结束 ###################//############ 例7 利用隐式参数进行隐式转换 #############object ImplicitParamsAsConvert{  // 以下函数行不通  // def smaller[T](a:T, b:T) = if(a < b) a else b //error  // 之前已经学过,可以使用上界或者视图界定来完成该功能,如下使用视图界定来实现  def smallerWithViewBound[T <% Ordered[T]](a: T, b: T) = if(a < b) a else b  // 上面是一种解决方法,使用转换函数也可以来达到这个目的:  // 下面方法的第二个参数是隐式参数,但是所要提供的隐式值其实又是一个隐式转换(函数),  // 它将T类型转换成Ordered[T]  // ord(a) < b 中的"<"是Ordered[T]中提供的方法,这个方法是:  // def < (that: A): Boolean = (this compare that) <  0  // "<"是Orderd[A]对象的成员,而"<"这个方法中传入的参数的类型是A  // 所以ord(a) < b 可以运行。  // 那么ord这个隐式参数的隐式值定义在哪里呢?它其实定义在Predef对象中,  // Predef对象会自动被import。Predef中定义了一些隐式转换函数,例如有:  // implicit def intWrapper(x: Int) = new runtime.RichInt(x)  // 它会将Int转化成RichInt,而RichInt中混入了Ordered[T]这个特质,所以可以使用"<"  def smaller[T](a: T, b: T)(implicit ord: T => Ordered[T]) = {    //if(ord(a) < b) a else b    //由于这边已经提供了一个隐式转换函数 ord: T => Ordered[T] ,所以ord(a)可以直接写成a,a会自动调用ord(a)    //如下:    if(a < b) a else b    //《快学scala》原文:ord是一个带单个参数的函数,被打上了implicit标签,并且有一个以单个标识符出现的名称。    //因此,它不仅是一个隐式参数,还是一个隐式转换。正因为这样,我们可以在函数体中省去对ord的调用。  }  def main(args: Array[String]) {    println(smallerWithViewBound(1,3))    smaller(1,3)    //如果想成功运行smaller(new Fraction(88,77), new Fraction(99,88))该怎么做?    //需要自己定义一个Fraction => Ordered[Fraction]的函数    import myscala.scala_for_the_impatient.implicits.Fraction._    println(smaller(new Fraction(99,88), new Fraction(88,77)))  }}//####################  例7 结束 ########################

package myscala.scala_for_the_impatient.implicitsimport java.io.Fileimport scala.io.Source/** * 隐式类 */object ContextHelper {  //隐式类,作用其实也是可以由隐式转换来完成的  implicit class FileRich(file: File) {    def read = Source.fromFile(file.getPath).mkString  }  implicit class Op(x: Int) {    def add(second: Int) = x + second  }  //隐式对象,没有构造器。可以继承一个父类,然后把它当成隐式值来使用  implicit object PointOrdering extends Ordering[Point]{    override def compare(a: Point, b: Point): Int =      if ((math.pow(a.x, 2) + math.pow(a.y, 2)) > (math.pow(b.x, 2) + math.pow(b.y, 2))) 1 else -1  }  //隐式值,功能跟上面的隐式对象是一样的  implicit val pointOrdering: Ordering[Point] = new Ordering[Point] {    override def compare(a: Point, b: Point): Int =      if ((math.pow(a.x, 2) + math.pow(a.y, 2)) > (math.pow(b.x, 2) + math.pow(b.y, 2))) 1 else -1  }}object ImplicitClass {  def main(args: Array[String]) {    import ContextHelper._    println(1.add(2))    println(new File("D:\\word.txt").read)  }}

package myscala.scala_for_the_impatient.implicits/** * 隐式对象 */abstract class Template[A] {  def add(x: A, y: A): A}abstract class SubTemplate[A] extends Template[A] {  def unit: A}object SubTemplate {  implicit object StringTemplate extends SubTemplate[String] {    override def unit: String = ""    override def add(x: String, y: String): String = x concat y  }  implicit object IntTemplate extends SubTemplate[Int] {    override def unit: Int = 0    override def add(x: Int, y: Int): Int = x + y  }}abstract class SubTemplateTwo[A] extends Template[A] {  def unit: A}object SubTemplateTwo {  //貌似与SubTemplate中的StringTemplate这个object功能一样  implicit val stringTemplate = new SubTemplateTwo[String] {    override def unit: String = ""    override def add(x: String, y: String): String = x concat y  }  //貌似与SubTemplate中的IntTemplate这个object功能一样  implicit val intTemplate = new SubTemplateTwo[Int] {    override def unit: Int = 0    override def add(x: Int, y: Int): Int = x + y  }}object ImplicitObject {  def main(args: Array[String]) {    def sum[A](xs: List[A])(implicit m: SubTemplate[A]): A =      if (xs.isEmpty) m.unit else m.add(xs.head, sum(xs.tail))    //sum方法在SubTemplate伴生对象中找到 SubTemplate[Int]这个值(对象),即IntTemplate。所以m就是IntTemplate    println(sum(List(1, 2, 3)))    //sum方法在SubTemplate伴生对象中找到 SubTemplate[String]这个值(对象),即StringTemplate。所以m就是StringTemplate    println(sum(List("I", " am", " lucius")))    def sumTwo[A](xs: List[A])(implicit m: SubTemplateTwo[A]): A =      if (xs.isEmpty) m.unit else m.add(xs.head, sumTwo(xs.tail))    println(sumTwo(List(1, 2, 3)))    println(sumTwo(List("I", " am", " lucius")))  }}

package myscala.scala_for_the_impatient.implicits/** * 7、上下文界定 * 类型参数可以有一个形式为T:M的上下文界定,其中M是另一个泛型类型。 * 它要求作用域存在一个类型为M[T]的隐式值。 * 例如: * class Pair[T : Ordering] * 要求存在一个类型为Ordering[T]的隐式值。该隐式值可以被用在该类的方法中。 */// 要求存在一个Ordering[T]的隐式值// 如果我们new一个Pair(3,4),编译器将推断出我们需要一个Pair[Int]。// 由于Predef作用域中已经有一个类型为Ordering[Int]的隐式值,因此Int满足上下文界定。// 这个Ordering[Int]就“成为该类的一个字段”,被传入需要该值的方法中。class Pair[T: Ordering](val first: T, val second: T) {  def smaller(implicit ord: Ordering[T]) = {    if (ord.compare(first, second) < 0) first else second  }}//如果不使用T:Ordering这个上下文界定,那么是不是可以写成下面的样子:class PairNotUseContextBound[T <% Ordered[T]](val first: T, val second: T) {  //作用域中存在一个Ordering[T],相当于该类的一个字段。  //这个ordering相当于上面Pair的作用域中的Ordering[T],Pair中的那个Ordering[T]相当于一个隐藏的字段。  private val ordering = new Ordering[T] {    override def compare(x: T, y: T): Int = if (x > y) 1 else -1  }  def smaller: T = smaller(ordering)  def smaller(ord: Ordering[T]): T = {    if (ord.compare(first, second) < 0) first else second  }}//如果你愿意,也可以使用Predef类的implicitly方法获取值:class PairUseImplicitly[T: Ordering](val first: T, val second: T) {  //但是这样的话,就不能显示地传入Ordering[T]这个对象了。  def smaller = if (implicitly[Ordering[T]].compare(first, second) < 0) first else second}// 或者,你也可以利用Ordered特质中定义的从Ordering到Ordered的隐式转换,一旦引入了这个转换,就可以使用关系操作符了(《快学Scala》的原文)// 感觉也可以理解成“从T到Ordered[T]的隐式转换”,因为这个隐式转换,需要一个参数T和一个隐式参数Ordering[T])// 内部的工作机制其实是这样的:// 在下例中,import Ordered._ 其实用到的是 import Ordered.orderingToOrdered// 我们再看一下orderingToOrdered这个隐式转换:/*object Ordered {  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的伴生对象中定义了一个隐式转换:orderingToOrdered,它将T类型转换成Ordered[T]类型,// 同时这个隐式转换还带了一个隐式参数,它的类型是Ordering[T]。PairImportOrdered[T: Ordering]的上下文界定// 确保必须在作用域中存在一个Ordering[T]的隐式值,而这个隐式值会传入到orderingToOrdered的隐式参数中。// 这样就把 T转换成了Ordered[T],而Ordered[T]中定义了关系操作符,所以就可以使用“<”了class PairImportOrdered[T: Ordering](val first: T, val second: T) {  def smaller = {    import Ordered._    //import Ordered.orderingToOrdered    if (first < second) first else second  }}class Point(val x: Double, val y: Double){  override def toString: String = s"(${x}, ${y})"}object Point {  //注意:下面两种写法都可以作为Ordering[Point]类型的隐式值  //写法一  implicit val pointOrdering: Ordering[Point] = new Ordering[Point] {    override def compare(a: Point, b: Point): Int =      if ((math.pow(a.x, 2) + math.pow(a.y, 2)) > (math.pow(b.x, 2) + math.pow(b.y, 2))) 1 else -1  }  //写法二  implicit object PointOrdering extends Ordering[Point]{    override def compare(a: Point, b: Point): Int =      if ((math.pow(a.x, 2) + math.pow(a.y, 2)) > (math.pow(b.x, 2) + math.pow(b.y, 2))) 1 else -1  }}object ContextBound {  def main(args: Array[String]) {    //smaller使用的是Predef中定义的Ordering[Int]的这个隐式值    /*   println(new Pair(3, 4).smaller) //ok       println(new PairNotUseContextBound(3, 4).smaller) //ok       println(new PairUseImplicitly(3, 4).smaller)*/    println(new PairImportOrdered(3, 4).smaller)    //smaller使用的是自己定义的order这个Ordering[Int]的隐式值    val order = new Ordering[Int] {      override def compare(x: Int, y: Int): Int = if (x > y) 1 else -1    }    new Pair(4, 3).smaller(order) //ok    //Point的伴生对象中有一个Ordering[Point]类型的隐式值(二选一即可),所以我们可以将Point传入到Pair中    println(new Pair(new Point(4, 2),new Point(3, 2)).smaller)  }}



0 0
原创粉丝点击