第十五章 Scala进阶——隐式转换与隐式参数

考虑如下场景:假设编写了一个向量类MyVector,并且包含了一些向量的基本操作。因为向量可以与标量做数乘运算,所以需要一个计算数乘的方法“ * ”,它应该接收一个类型为基本值类的参数。在向量对象myVec调用该方法时,可以写成诸如“myVec * 2”的形式。在数学上,反过来写“2 * myVec”也是可行的,但是在程序里却行不通。因为操作符的左边是调用对象,反过来写就表示Int对象“2”是方法的调用者,但是Int类里并没有这种方法。

为了解决上述问题,所有的oop语言都会有相应的策略,比如笔者熟悉的C++是通过友元的方式来解决。Scala则是采取名为“隐式转换”的策略,也就是把本来属于Int类的对象“2”转换类型,变成MyVector类的对象,这样它就能使用数乘方法。隐式转换属于隐式定义的一种,隐式定义就是那些程序员事先写好的定义,然后允许编译器隐式地插入这些定义来解决类型错误。因为这部分定义通常对使用者不可见,并且由编译器自动调用,故而得名“隐式定义”。

一、隐式定义的规则

Scala对隐式定义有如下约束规则:

①标记规则。只有用关键字“implicit”标记的定义才能被编译器隐式使用,任何函数、变量或单例对象都可以被标记。其中,标记为隐式的变量和单例对象常用作隐式参数,隐式的函数常用于隐式转换。比如,代码“x + y”因为调用对象x的类型错误而不能通过编译,那么编译器会尝试把代码改成“convert(x) + y”,其中convert是某种可用的隐式转换。如果convert能将x改成某种支持“+”方法的对象,那么这段代码就可能通过类型检查。

②作用域规则。Scala编译器只会考虑在当前作用域内的隐式定义,否则,所有隐式定义都是全局可见的将会使得程序异常复杂甚至出错。隐式定义在当前作用域必须是“单个标识符”,即编译器不会展开成“A.convert(x) + y”的形式。如果想用A.convert,那么必须先用“import A.convert”导入才行,然后被展开成“convert(x) + y”的形式。单个标识符规则有一个例外,就是编译器会在与隐式转换相关的源类型和目标类型的伴生对象里查找隐式定义。因此,常在伴生对象中定义隐式转换,而不用在需要时显式导入。

③每次一个规则。编译器只会插入一个隐式定义,不会出现“convert1(convert2(x)) + y”这种嵌套的形式,但是可以让隐式定义包含隐式参数来绕开这个限制。

④显式优先原则。如果显式定义能通过类型检查,就不必进行隐式转换。因此,总是可以把隐式定义变成显式的,这样代码变长但是歧义变少。用显式还是隐式,需要取舍。

此外,隐式转换可以用任意合法的标识符来命名。有了名字后,一是可以显式地把隐式转换函数写出来,二是明确地导入具体的隐式转换而不是导入所有的隐式定义。

Scala只会在三个地方使用隐式定义:转换到一个预期的类型,转换某个选择接收端(即调用方法或字段的对象),隐式参数。

二、隐式地转换到期望类型

Scala的编译器对于类型检查比较严格,比如把一个浮点数赋值给整数变量,通常情况下人们可能希望通过截断小数部分来完成赋值,但是Scala在默认情况下是不允许这种丢失精度的转换的,这会造成类型匹配错误。例如:

scala> val i: Int = 1.5
<console>:11: error: type mismatch;
 found   : Double(1.5)
 required: Int
       val i: Int = 1.5
                    ^

但是用户可能并不关心精度问题,确实需要这样一种赋值操作,那么就可以通过定义一个隐式转换来完成。例如:

scala> import scala.language.implicitConversions
import scala.language.implicitConversions

scala> implicit def doubleToInt(x: Double) = x.toInt
doubleToInt: (x: Double)Int

scala> val i: Int = 1.5
i: Int = 1 

此时再进行之前的赋值,就会正确地截断小数部分。当然,隐式转换也可以显式地调用:

scala> val i: Int = doubleToInt(2.33)
i: Int = 2 

第七章讲解类继承时,最后提到了Scala的全局类层次,其中就有七种基本值类的转换,比如Int可以赋值给Double。这其实也是隐式转换在起作用,只是这个隐式转换定义在scala包里的单例对象Predef里。因为所

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值