标签(空格分隔): hadoop scala
带着问题去学习
什么是隐式转换,隐式参数?
有什么用?
隐式转换也是一种增强,丰富现有类库功能的一种方式
怎么使用?
隐式转换的触发条件,和非触发条件?
隐式转换
如果是使用过动态代理或者aop,或者装饰着模式 那么应该知道在许多情况下我们可以通过这些方法进行类功能增强。
而隐式转换也是一种增强,丰富现有类库功能的一种方式。
隐式转换函数
隐式转换函数:就是以关键字implict定义的带有单个参数函数。
例如:
implicit def int2Double(x: Int) = x.toDouble
隐式转换函数如它的名字一样,功能就是将类型进行隐式的转换。
通过这种转换我们就可以丰富现有类库的功能。
下面我们用一下scala对应的File增强的例子。
首先是增强类
class RichFile(file: File) {
def read = Source.fromFile(file.getPath).mkString
}
接着是要使用的对象
object EnhandTestObject{
def main(args: Array[String]): Unit = {
val file = new File("D:\\词典\\stopword.dic") //stopword.dic就是一个普通的txt里面有内容
implicit def file2RichFile(file :File) = new RichFile(file)//在当前作用域中直接引用
//import com.lcy.hello.impl_enhance.EnHanceFile._//或者引入某个对象中的隐式转换函数,下面就是写的隐式转换对象
file.read
}
}
还有一个是包含隐式转换的对象
object EnHanceFile {
implicit def file2RichFile(file :File) = new RichFile(file)
implicit def int2Double(x: Int) = x.toDouble
}
这里有两个知识点需要说下:隐式转换流程?引入隐式转换的方式都有几种?
1)怎么转换了需要定义以下哪几个步骤(流程)
a.首先我们需要定义一个增强类 就是上面说的RichFile 人家就是拿到File提供一个read方法,可直接通过read完成文件读取。
b.增强类有了,我们需要引用这个增强类,那么引用的过程我们通过隐式转换,怎么用呢,我们可以直接将隐式转换放在作用域上,比如 EnhandTestObject的main方法中直接通过 implict定义隐式函数即可,接着就可以完成file.read的调用,因为file被隐式转换成了RichFile了。
c.或者我们搞一个object对象 存放隐式转换函数,我们可以通过 import com.lcy.hello.impl_enhance.EnHanceFile._ 的形式去引入这个隐式函数,后面的“_” 不可少 后面再介绍这个引入。
2)隐式转换的使用方式有几种
刚刚1)中说了 我们可以直接在作用域中定义是一种。 第二种是通过一个对象去存放隐式转换函数我们通过import导入进来也可使用。
上面的两种都是在作用域中引入了,另外一种就是如果这个类的伴生对象存在隐式转换也是会被使用的。这个在隐式转换规则我们下面再说。
隐式转换规则(是不是导入隐式转换就一定会转换)
隐式转换也有规则的不是所有的牛奶都叫特仑苏。。。错了 不是所有的隐式转换都能够转换成功。下面说下原则(人家隐式转换也是讲原则的)
比如刚刚的
第一种情况 如果访问file.read成员不存在 file作为调用对象就会被转换成RichFile(前提是有引入隐式转换啊下面不强调),这里是作为调用提供方
new File(“README.md”).read
第二种情况
比如我们定义了一个方法:
def printlnDou(x: Double) ={
println("x=" +x)
}
//然后我们在main方法中这么写
val x: Int = 1
printlnDou(x)
//编译通过,运行通过你敢信 你再去看下会发现Int的伴生对象存在隐式转换函数
implicit def int2double(x : scala.Int) : scala.Double = { /* compiled code */ } 这个我们不需要引入人家都可以进行隐式转换,Int伴生对象存在隐式函数
所以说明下,如果作为参数使用,而本身不符合类型的话 也会被隐式转换的。
第三种情况
如果这个类木有这个方法,而隐式转换后存在则会被隐式转换,跟第一种我理解起来差不多。
另外还有三种情况编译的时候不会被隐式转换的情况。
1)如果本身就能完成编译不会被隐式转换。这个好理解
2)二义性:如果一个参数会有两种隐式转换情况,这种情况会报错比如 a*b 既可以convert1(a)*b 又可以 convert2(a) b
3)编译器不会进行二次转换 比如 正常ab 编译器不会进行 convert1(convert2(a))*b
针对2)点,这里说的是针对的是一个对象有两种转换的情况。如果存在
convet1(a) * convert2(b)的情况下,是可以的,而且是 a* convert2(b)会被转换,因为a是属于被调用的对象它不用转换的优先级高。
隐式参数
什么是隐式参数?
隐式参数就是参数上面有implict标识符,强调一下隐式参数在一个括号中只能有一个
怎么用?
隐式参数可以让我们的方法调用变得更简洁,可以可以直接不用传递参数,利用 作用域内的implict的变量,或者引入,或者伴生对象中的参数自动传入。
下面是一个例子:
我们在对象中定义一个包含隐式参数的方法。
def quote(str:String)(implicit del:Delimiter): Unit ={
println(del.left + str + del.right)
}
这个方法调用我们有两种方式,一种是显示调用,一种是通过隐式调用。
比如显示调用:
def main(args: Array[String]): Unit = {
//显示调用
// quote("我很好")(new Delimiter("<",">"))
}
或者在当前伴生对象中定义一个隐式对象
implicit val deli = new Delimiter("<",">")
然后通过隐式调用
def main(args: Array[String]): Unit = {
quote("我很好")
}
隐式参数的另一种使用方式,可达成隐式转换
我们用下scala上面的例子加入我想比较两个数据的大小
编译不通过,因为咱们a是否有这个<的方法是没法确定的。
def smaller[T](a: T,b:T) = if(a < b) a else b
我们可以通过Ordered对象完成转换成Ordered对象就可以调用<方法了,而这里我们就可以通过一个隐式参数完成这个操作,其实也就是一个函数完成这个操作只不过这个函数是隐式参数
def smaller[T](a: T,b:T)(implicit order: T => Ordered[T]) = if(order(a) < b) a else b
之后定义一个隐式函数
implicit val comFun = (a :CompareClass) => new Ordered[CompareClass]{
override def compare(that: CompareClass): Int = {
if(a.a > that.a){
1
}else if(a.a < that.a) -1 else 0
}
override def <(that: CompareClass): Boolean = {
if(a.a < that.a) true else false
}
}
//main方法调用
def main(args: Array[String]): Unit = {
val a = new CompareClass(1);
val b = new CompareClass(2);
println(smaller(a,b))
}
这里会有隐式转换将会吧CompareClass转换成Ordered对象这样就可以完成比较了。