一 隐式转换
隐式转换函数,也被称作隐式视图,它是可以把一种类型转换成另外一种类型,进而可以使用另外一种类型的属性和方法,从而满足表达式的要求
语法格式:implicit def 函数名(参数名:参数类型):返回类型 = {}
作用:如果隐式作用域里存在这个定义,它会隐式地把原始类型的值转化为增强的类型的值(在需要的时候)
比如现在有两个没有关系的类Person 和 Animal
class Person(val name:String, val age:Int) {
def desc = println("My name is "+name+", and my age is "+age)
def shopping(p:Person) = println("I'm shopping with "+p.name)
}
class Animal(val name:String, val age:Int) {
def desc = println("My name is "+name+", and my age is "+age)
}
我们知道Person有shopping方法,但是Animal没有,怎么办呢?
我们就可以在一个隐式作用域,什么是隐式作用域:
就是定义隐式函数的地方,可以是一个类或者object或者一个方法中,注意你不能定义在需要被转换的类中,然后到其他地方使用这个类,是不会自动转换的,除非你手动显示的转换
class Join {
implicit def dog2Person(obj:Object):Person = {
if (obj.getClass == classOf[Animal]) {
val _dog = obj.asInstanceOf[Animal]
new Person(_dog.name,_dog.age)
} else null
}
def join(): Unit ={
val p1 = new Person("nicky",29)
val p2 = new Person("baobao",27)
val dog = new Animal("dog",4)
val cat = new Animal("cat",5)
dog.shopping(cat)
dog.shopping(p2)
}
}
object ImplicitClient extends App {
implicit def dog2Person(obj:Object):Person = {
if (obj.getClass == classOf[Animal]) {
val _dog = obj.asInstanceOf[Animal]
new Person(_dog.name,_dog.age)
} else null
}
val p1 = new Person("nicky",29)
val p2 = new Person("baobao",27)
val dog = new Animal("dog",4)
val cat = new Animal("cat",5)
dog.shopping(cat)
dog.shopping(p2)
}
如果你是定义在被转换的类里面:
class Animal(val name:String, val age:Int) {
def desc = println("My name is "+name+", and my age is "+age)
implicit def dog2Person(obj:Object):Person = {
if (obj.getClass == classOf[Animal]) {
val _dog = obj.asInstanceOf[Animal]
new Person(_dog.name,_dog.age)
} else null
}
}
但是又在其他地放创建该对象,那么隐式函数是不会生效的。
object ImplicitClient extends App {
val p1 = new Person("nicky",29)
val p2 = new Person("baobao",27)
val dog = new Animal("dog",4)
val cat = new Animal("cat",5)
dog.shopping(cat)
dog.shopping(p2)
}
除非你显示的转换
object ImplicitClient extends App {
val p1 = new Person("nicky",29)
val p2 = new Person("baobao",27)
val dog = new Animal("dog",4)
//除非你这样显示转换
val d = dog.dog2Person(dog)
d.shopping(p1)
}
二 隐式转换函数的作用域和导入
在scala中,隐式解析一般分为两个阶段:
2.1:隐式转换的作用域中查找
首先在隐式转换的作用域中查找,隐式转换的作用域已经介绍了。就是在定义隐式函数的作用域中,可以是方法或者类或者object中。
2.2:到相应的源或者目标类的伴生对象中查找
其次如果隐式作用域没有找到,那么编译器就会自动到相应的源或者目标类的伴生对象中查找
源类2目标类:即源类希望被转换城目标类,从而调用目标类的方法
第一种情况:去源类的伴生类中查找
class Animal(val name:String, val age:Int) {
def desc = println("My name is "+name+", and my age is "+age)
}
object Animal{
implicit def dog2Person(obj:Object):Person = {
if(obj.getClass == classOf[Animal]) {
val _dog = obj.asInstanceOf[Animal]
new Person(_dog.name,_dog.age)
} else null
}
}
这时候发现,可以初始化完毕源类之后,源类转化成目标类,就可以调用目标类的方法了
第二种情况:到目标类的伴生对象中查找,也就需要转换成的那个类型的伴生对象
class Person(val name:String, val age:Int) {
def desc = println("My name is "+name+", and my age is "+age)
def shopping(p:Person) = println("I'm shopping with "+p.name)
}
object Person{
implicit def dog2Person(obj:Object):Person = {
if (obj.getClass == classOf[Animal]) {
val _dog = obj.asInstanceOf[Animal]
new Person(_dog.name,_dog.age)
} else null
}
}
这时候发现,可以初始化完毕源类之后,源类转化成目标类,仍然不可以调用目标类的方法了。怎么回事了?因为这样是不够的,你还需要在初始化的时候显示静态导入目标类,什么是静态导入,就是导入类中的所有成员:
import com.scala.implicits.Person._
这时候才可以调用到目标类的方法
object ImplicitClient extends App {
import com.scala.implicits.Person._
val p1 = new Person("Lice",10)
val dog = new Animal("小花",2)
//首先这不是在隐式的作用域中,从源类查找
dog.shopping(p1)
}
注意点:
# 不能直接将隐式转换函数放入到源类或者目标类中,那样是没有效果的
# 不能将隐式转换函数放入到目标类,我们还需要在需要的时候静态导入目标类
三 隐式转换规则或者时机
3.1 表达式的类型与预期的不一致
math.sqrt(Fraction(2,4))
sqrt本身是期望传入一个Double类型,但是你确传入了一个Fraction对象,那么就会调用Fraction的隐式转换
3.2 当对象访问一个不存在的成员时候
就如前面的例子所讲,初始化Animal,然后调用不属于自己的shopping方法,这时候,就会进行隐式转换
object ImplicitClient extends App {
import com.scala.implicits.Person._
val p1 = new Person("Lice",10)
val dog = new Animal("小花",2)
//Animal对象调用不属于自己的方法
dog.shopping(p1)
}
3.3 当对象调用某个方法,该方法存在,但是方法参数不相同
object ImplicitClient extends App {
val p1 = new Person("Lice",10)
val dog = new Animal("小花",2)
//Animal本身没有desc(color:String,weight:Int)方法的,如果他调用了这个方法,就会进行隐式转换
dog.desc("白色",5);
// My color is 白色, and my weight is 5
}
四 隐式参数
在普通的函数或者方法中,可以带有一个隐式的参数列表:
注意:
# 如果程序显示提供,虽然隐式的,但是不会去隐式作用域查找,因为已经提供了参数
# 如果没有显示提供,那么首先会在隐式作用域查找 implicit 修饰val 或者 函数,val是该类型的变量,函数是返回该类型的函数
# 如果隐式作用域没有,则去隐式参数类型相对应的伴生对象中查找implicit 修饰的val 或者函数,val是该参数类型的变量,函数是返回该类型的函数
首先定一个带有隐式参数的函数:
case class Delimters(left:String,right:String) {
def quote(what:String) (implicit delims:Delimters) = println(delims.left + what + delims.right)
}
/*创建Delimiters实例,并赋给一个变量*/
val d1 = Delimters("Left","Right")
/*
* 编译会报错 Error:(9, 12)could not find implicit value for parameter
* delims:com.scala.implicits.Delimters
* d1.quote("-what-")
*/
原因在于,你既没有显示提供该值,又没在隐式作用域或者该参数类型的伴生对象中查找到该隐式值
显示提供该参数
/*它既然提示找不到隐式值,那我们给他一个显示的delims*/
val d2 = Delimters("<",">")
/*这样是肯定没有问题的,结果: <what>,但是这样就根本就没有用到隐式参数*/
d1.quote("what")(d2) //柯里化
/**
* 隐式参数的作用就是,如果没有提供该值,那么就会自动从隐式作用域去查找该参数值
* 现在我们讨论隐式参数是如何查找的
*/
/*
* 首先在当前作用域,需要提供一个隐式参数的变量或者函数
* 该变量或者函数必须有implicit关键字修饰,否则查不到
* 但是不能同时提供2个,只能提供一个,否则报错
*/
implicit val d3 = Delimters("<",">")
/*我们并没有提供Delimters的参数,但是它查到了d3,结果<UserName>*/
d1.quote("UserName")
implicit def delims = Delimters("[","]")
/*我们并没有提供Delimters的参数,但是它查到了delims函数,结果[Address]*/
d1.quote("Address")
/**
* 如果当前作用域没有查到,他就会到要求的类型的伴生对象查找
* 当然前提还是必须声明为implicit 的 val 变量或者函数
*/
object Delimters{
/*通过变量提供该类型的隐式值*/
implicit val d = Delimters("{","}")
}
d1.quote("Connection")
object Delimters{
/*通过函数提供该类型的隐式值*/
implicit def delims = Delimters("<<",">>")
}
d1.quote("Connection")
五 利用隐式参数进行隐式转换
我们先看一个场景:
def small[T](a:T,b:T):T = {
if (a < b) a else b;
}
由于T 我们并不知道他是否可以应用比较符,所以这段代码编译是有问题的。
那我们可以提供一个转换函数:
def small[T](a:T,b:T)(implicit order:T => Ordered[T]):T = {
if (a < b) a else b;
}
由于Ordered[T]trait可以接收一个类型为T作为参数的比较操作,所以编译是没有问题的