隐式操作规则
隐式定义是指编译器为了修正类型错误而允许插入到程序中的定义。比如,x + y不能通过类型检查,此时,如果有某个可用的隐式转换convert,那么编译器会把它改成convert(x) + y。
隐式转换通用规则:
- 标记规则:只有标记为implicit的定义才是可用的,它可以标记任何 变量 函数 或者 对象定义,比如隐式函数定义:
implicit def intToString (i: String) = i.toString
- 作用域规则:插入的隐式转换必须以单一标识符的形式处于作用域中,或与转换的源或目标类型关联在一起,scala编译器仅考虑处于作用域之内的隐式转换。
- 无歧义规则:隐式转换唯有不存在其他可插入转换的前提下才能插入,比如不能存在既可使用convert1(x) + y,又可使用convert2(x) + y的情况。
- 单一调用规则:只会尝试一个隐式操作。编译器不会把 x + y重写成convert1(convert2(x)) + y。不会尝试在某个隐式操作期间再添加隐式转换,然而,可以通过让隐式操作带隐式参数绕过这个限制。
- 显示操作先行规则:若编写的代码类型检查无误,则不会尝试任何隐式操作。
命名隐式转换:隐式转换可以任意命名,命名仅需要考虑两种情况,你是否在方法应用中明确写明,以及决定哪个隐式转换在程序的任何地方都有效。
比如第二点,对象带有两个隐式转换:
object Convert {
implicit def stringWrapper(s: String): RandomAccessSeq[Char] = ...
implicit def intToString(i: Int): String = ...
}
你可以使用像这样使用,从而有选择性的只引用一个:
import Convert.intToString
...
scala中能用到隐式操作的有三个地方:转换为期望类型 ,指定(方法)调用者的转换 ,隐式参数 。
隐式转换为期望类型
很简单,当编译器看见X时发生了错误,需要Y时,就会坚持从X到Y的隐式转换函数。
比如要修正 val i: Int = 3.5
的错误,可以定义这样的隐式转换消除障碍:
implicit def doubleToInt (d: Double) = d.toInt
转换(方法调用的)接受者
隐式转换还应用于方法调用的接收者,也就是方法调用的对象。这种隐式转换主要有两种用途,一:接收者转换使得新的类可以更为平滑地集成到现存类层级中。二:支持编写域特定语言(DSL)。
1 与新类型的交互操作
class Number (n: Int) {
def + (that: Number): Number = ...
def + (that: Int): Number = ...
}
> val num = new Number(1)
/*
显然,num + 1表达式正确
但是1 + num就会抛出错误,
为了允许这种混合的运算需要定义Int到Number的隐式转换
*/
implicit def intToNumber (i: Int) = new Number(i)
> 1 + num //OK
/*
其实就是编译器检查失败后,搜索到了从Int到Number的类型转换,并应用了方法,等价于
intToNumber(1) + num
*/
2 模拟新的语法
scala里,可以这样创建Map:
Map(1 -> "one", 2 -> "two")
其实就是元组的写法,等价于:
Map((1, "one"), (2, "two"))
以下是实现的相关定义:
package scala
object Predef {
...
class ArrowAssoc[A] (x: A) {
def -> [B](y: B): Tuple2[A, B] = Tuple2(x, y)
}
implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A] = new ArrowAssoc(x)
...
}
隐式参数
通过参数列表可以让编译器插入隐式操作,编译器有时会用call(a)(b)替代call(a), 或者用new obj(a)(b)替代new obj(a),从而通过添加缺失的参数列表以满足函数的调用,被提供的是完整的最后一节 柯里化 参数,而不仅是最后的参数,例如call(a)(b, c, d)替代call(a),并且这最后一组参数列表必须被标记为implicit。
class A (val name: String)
...
def func (id: Int)(implicit p: A) = ...
val a = new A("abc")
func(1)(a) //此时只能显示调用
implicit val aa = new A("cba")
func(1)(aa) or func(1)
scala隐式转换及其DSL实现(二)将介绍DSL实现的具体应用。
网友评论