隐式转换、隐式参数、上下文界定以及隐式类 学习笔记
来源:互联网 发布:淘宝退货率高会怎样 编辑:程序博客网 时间: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
- 隐式转换、隐式参数、上下文界定以及隐式类 学习笔记
- 第62讲:Scala中上下文界定内幕中的隐式参数与隐式参数的实战详解及其在Spark中的应用源码解析学习笔记
- Scala隐式转换——视图界定
- Spark学习笔记5-隐式转换,隐式参数,隐式类
- Scala学习笔记--视图界定
- scala总结(4) -- 隐式转换以及隐式参数
- Scala学习笔记6 - 隐式转换和隐式参数
- 12.dubbo回声测试、上下文信息、隐式参数
- scala学习笔记(十六) 类型参数与隐式转换
- Spark进阶视频之Scala中上下文界定内幕中的隐式参数与隐式参数的实战详解及其在Spark中的应用源码解析
- 62.Scala中上下文界定内幕中的隐式参数与隐式参数的实战详解及其在Spark中的应用源码解析
- 第60讲:Scala中隐式参数实战详解以及隐式参数在Spark中的应用源码解析学习笔记
- scala学习之:隐式转换与隐式参数
- Scala学习—隐式转换与隐式参数
- 快学Scala学习笔记及习题解答(21-22隐式转换和隐式参数、定界延续)
- scala 隐式转换参数
- 第59讲:Scala中隐式转换初体验实战详解以及隐式转换在Spark中的应用源码解析学习笔记
- Scala深入浅出进阶经典 第62讲:Scala中上下文界定内幕中的隐式参数与隐式参数的实战详解及其在Spark中的应用源码解析
- mysql: update from set
- Java Clone, Shallow Copy and Deep Copy
- 一步一图一代码,一定要让你真正彻底明白红黑树
- 大文件MD5值计算
- JSTL标签用法:<c:choose><c:forEach><c:if><c:when><c:set>
- 隐式转换、隐式参数、上下文界定以及隐式类 学习笔记
- ORA-00304: requested INSTANCE_NUMBER is busy
- Android如何监听蓝牙耳机的按键事件
- SpringMvc一个简单的框架入门程序
- UML Section Four 行为图
- web网站中腾讯空间分享不了的问题
- struts2 spring4 图片上传
- 设计模式:单例模式(Singleton Pattern)
- hdu 4407 SUM(容斥原理)