经过隐式转换,程序员能够在编写Scala程序时故意漏掉一些信息,让编译器去尝试在编译期间自动推导出这些信息来,这种特性能够极大的减小代码量,忽略那些冗长,过于细节的代码。html
隐式转换是Scala的一大特性, 若是对其不是很了解, 在阅读Spark代码时候就会很迷糊,有人这样问过我?git
RDD这个类没有reduceByKey,groupByKey等函数啊,而且RDD的子类也没有这些函数,可是好像PairRDDFunctions这个类里面好像有这些函数 为何我能够在RDD调用这些函数呢?程序员
答案就是Scala的隐式转换; 若是须要在RDD上调用这些函数,有两个前置条件须要知足:github
参考SparkContext Object, 咱们发现其中有上10个xxToXx类型的函数:apache
implicit def intToIntWritable(i: Int) = new IntWritable(i) implicit def longToLongWritable(l: Long) = new LongWritable(l) implicit def floatToFloatWritable(f: Float) = new FloatWritable(f) implicit def rddToPairRDDFunctions[K, V](rdd: RDD[(K, V)]) (implicit kt: ClassTag[K], vt: ClassTag[V], ord: Ordering[K] = null) = { new PairRDDFunctions(rdd) }
这么一组函数就是隐式转换,其中rddToPairRDDFunctions,就是实现:隐式的将RDD[(K, V)]类型的rdd转换为PairRDDFunctions对象,从而能够在原始的rdd对象上 调用reduceByKey之类的函数;类型隐式转换是在须要的时候才会触发,若是我调用须要进行隐式转换的函数,隐式转换才会进行,不然仍是传统的RDD类型的对象;函数
还说一个弱智的话,这个转换不是可逆的;除非你提供两个隐式转换函数; 这是你会说,为何我执行reduceByKey之后,返回的仍是一个rdd对象呢? 这是由于reduceByKey函数 是PairRDDFunctions类型上面的函数,可是该函数会返回一个rdd对象,从而在用户的角度没法感知到PairRDDFunctions对象的存在,从而精简了用户的认识, 不知晓原理的用户能够把reduceByKey,groupByKey等函数当着rdd自己的函数oop
上面是对spark中应用到隐式类型转换作了分析,下面我就隐式转换进行总结;ui
从一个简单例子出发,咱们定义一个函数接受一个字符串参数,并进行输出spa
def func(msg:String) = println(msg)
这个函数在func("11")调用时候正常,可是在执行func(11)或func(1.1)时候就会报error: type mismatch的错误. 这个问题很好解决.net
上面两个方法使用的是传统JAVA思路,虽然均可以解决该问题,可是缺点是不够简洁;在充满了语法糖的Scala中, 针对类型转换提供了特有的implicit隐式转化的功能;
隐式转化是一个函数, 能够针对一个变量在须要的时候,自动的进行类型转换;针对上面的例子,咱们能够定义intToString函数
implicit def intToString(i:Int)=i.toString
此时在调用func(11)时候, scala会自动针对11进行intToString函数的调用, 从而实现能够在func函数已有的类型上提供了新的类型支持,这里有几点要说一下:
上面咱们看到的例子是将函数的参数从一个类型自动转换为一个类型的例子,在Scala中, 除了针对函数参数类型进行转换之外,还能够对函数的调用者的类型进行转换.
好比A+B,上面咱们谈到是针对B进行类型自动转换, 其实能够在A上作类型转换,下面咱们拿一个例子来讲明
class IntWritable(_value:Int){ def value = _value def +(that:IntWritable): IntWritable ={ new IntWritable(that.value + value) } } implicit def intToWritable(int:Int)= new IntWritable(int) new IntWritable(10) + 10
上面咱们首先定义了一个类:IntWritable, 并为int提供了一个隐式类型转换intToWritable, 从而可使得IntWritable的+函数在原先只接受IntWritable类型参数的基础上, 接受一个Int类型的变量进行运算,即new IntWritable(10) + 10能够正常运行
如今换一个角度将"new IntWritable(10) + 10" 换为"10 + new IntWritable(10)"会是什么结果呢?会报错误吗?
按道理是应该报错误,首先一个Int内置类型的+函数,没有IntWritable这个参数类型; 其次,咱们没有针对IntWritable类型提供到Int的隐式转换, 即没有提供writableToInt的implicit函数.
可是结果是什么?10 + new IntWritable(10)的是能够正常运行的,并且整个表达的类型为IntWritable,而不是Int, 即Int的10被intToWritable函数隐式函数转换为IntWritable类型;
结论:隐式转换能够针对函数参数类型和函数对象进行类型转换; 如今问题来了,看下面的例子
implicit def intToWritable(int:Int)= new IntWritable(int) implicit def writableToInt(that:IntWritable)=that.value val result1 = new IntWritable(10) + 10 val result2 = 10 + new IntWritable(10)
在上面的IntWritable类的基础上,咱们提供了两个隐式类型转换函数, 即Int和IntWritable之间的双向转换;这样的状况下result1和result2两个变量的类型是什么?
答案:result1的类型为IntWritable, result2的类型Int;很好理解, result1中的Int类型的10被intToWritable隐式转换为IntWritable;而result2中的IntWritable(10)被writableToInt 隐式转换为Int类型;
你确定会问?result2中为何不是像上面的例子同样, 把Int类型的10隐式转换为IntWritable类型呢?缘由就是隐式转换的优先级;
发生类型不匹配的函数调用时, scala会尝试进行类型隐式转换;首先优先进行函数参数的类型转换,若是能够转换, 那么就完成函数的执行; 不然尝试去对函数调用对象的类型进行转换; 若是两个尝试都失败了,就会报方法不存在或者类型不匹配的错误;
OK, Scala的隐式转换是Scala里面随处可见的语法, 在Spark中也很重要, 这里对它的讲解,算是对Shuffle作一个补充了, 即一个RDD之因此能够进行基于Key的Shuffle操做 是由于RDD被隐式转换为PairRDDFunctions类型。
1.将方法或变量标记为implicit
2.将方法的参数列表标记为implicit
3.将类标记为implicit
Scala支持两种形式的隐式转换:
隐式值:用于给方法提供参数
隐式视图:用于类型间转换或使针对某类型的方法能调用成功
例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 ^
scala> implicit val p = "mobin" //p被称为隐式值 p: String = mobin scala> person res1: String = mobin
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") //蛙泳 }
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 (<argument> : 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.做用域内不能有与之相同名称的标示符
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) }
编译器在mobin对象调用increment时发现对象上并无increment方法,此时编译器就会在做用域范围内搜索隐式实体,发现有符合的隐式类能够用来转换成带有increment方法的StringImprovement类,最终调用increment方法。
1.当方法中的参数的类型与目标类型不一致时
2.当对象调用类中不存在的方法或成员时,编译器会自动将对象进行隐式转换
即编译器是如何查找到缺失信息的,解析具备如下两种规则:
1.不存在二义性(如例1)
2.隐式操做不能嵌套使用,即一次编译只隐式转换一次(One-at-a-time Rule)
Scala不会把 x + y 转换成 convert1(convert2(x)) + y
- https://github.com/ColZer/DigAndBuried/blob/master/spark/scala-implicit.md
- https://blog.csdn.net/jameshadoop/article/details/52337949
- https://www.cnblogs.com/MOBIN/p/5351900.html