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 的编译过程中,编译器依次做了以下这些事情:

  1. 根据方法签名,在 JsonPatch 类中寻找一个 toCommonPatch 方法,并且接受空的参数列表,失败。
  2. 寻找一个潜在的,作用域中拥有从 JsonPatch -> XXX 类的隐式转换,并且这个 XXX 类具有需要的方法签名,找到了 JsonPatchExt 类。
  3. 进行一次类型转换,将目标对象 jsPatch 转为相应的 JsonPatchExt 对象,调用其 toCommonPatch 方法,类似于:transform(jsPatch).toCommonPatch,当然,transform 的实现被编译器隐藏了。

 

3. 个人习惯/代码规范

针对这种 implicit class 的定义,一般来说,我个人习惯遵循以下两个原则:

  • 根据适用的作用域,定义在 伴生对象 object 内部,或者 package object 中。
  • 类名一般定义为 XxxExt 或者 RichXxx。

 

posted @ 2021-05-20 16:21  Gerrard_Feng  阅读(273)  评论(0编辑  收藏  举报