scala基础之隐式转换

一 隐式转换

隐式转换函数,也被称作隐式视图,它是可以把一种类型转换成另外一种类型,进而可以使用另外一种类型的属性和方法,从而满足表达式的要求

语法格式: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作为参数的比较操作,所以编译是没有问题的

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫言静好、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值