scala隐式转换及其DSL实现(一)

隐式操作规则

隐式定义是指编译器为了修正类型错误而允许插入到程序中的定义。比如,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实现的具体应用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容