Scala 进阶(2)—— implicit 用法:隐式转换
1. 转换接收端
在 Scala 中,implicit 的一个常见用法,是作为方法调用的接收端,如果觉得这种说法过于晦涩的话,我的理解是:
- implicit 可以动态地为目标对象增加函数。
我们首先看一段例子:
implicit class JsonPatchExt(underlying: playJson.JsonPatch) { /** * Transforms a playJsonPatch [[playJson.JsonPatch]] into a common [[Patch]] used in this project. * * @return a list of [[Patch]]es representing the list of diffson operations. */ def toCommonPatch: Seq[Patch] = underlying.ops.collect { case Add(path, value) => Patch.add(path.serialize, Some(value)) case Replace(path, value, _) => Patch.replace(path.serialize, Some(value)) case Remove(path, _) => Patch.remove(path.serialize) } }
这里我们定义了一个 implicit class。
在原先的 source code 中,playJson.JsonPatch 这个类,并没有定义 toCommonPatch 这个方法,而我们通过 implicit class 为 JsonPatch 增加了 toCommonPatch 方法。
2. 原理
它是怎么做到的?
假设现在有个调用端,有一个 jsPatch 的 JsonPatch 对象,用了 jsPatch.toCommonPatch 方法。
在 Scala 的编译过程中,编译器依次做了以下这些事情:
- 根据方法签名,在 JsonPatch 类中寻找一个 toCommonPatch 方法,并且接受空的参数列表,失败。
- 寻找一个潜在的,作用域中拥有从 JsonPatch -> XXX 类的隐式转换,并且这个 XXX 类具有需要的方法签名,找到了 JsonPatchExt 类。
- 进行一次类型转换,将目标对象 jsPatch 转为相应的 JsonPatchExt 对象,调用其 toCommonPatch 方法,类似于:transform(jsPatch).toCommonPatch,当然,transform 的实现被编译器隐藏了。
3. 个人习惯/代码规范
针对这种 implicit class 的定义,一般来说,我个人习惯遵循以下两个原则:
- 根据适用的作用域,定义在 伴生对象 object 内部,或者 package object 中。
- 类名一般定义为 XxxExt 或者 RichXxx。