代码之家  ›  专栏  ›  技术社区  ›  joesan

Scala类型类理解接口语法

  •  3
  • joesan  · 技术社区  · 8 年前

    我在阅读有关cats的文章时,遇到了下面的代码片段,它是关于将对象序列化为JSON的!

    它从这样一个特征开始:

    trait JsonWriter[A] {
      def write(value: A): Json
    }
    

    在此之后,还有一些域对象的实例:

    final case class Person(name: String, email: String)
    
    object JsonWriterInstances {
      implicit val stringWriter: JsonWriter[String] =
        new JsonWriter[String] {
          def write(value: String): Json =
            JsString(value)
        }
    
      implicit val personWriter: JsonWriter[Person] =
        new JsonWriter[Person] {
          def write(value: Person): Json =
            JsObject(Map(
              "name" -> JsString(value.name),
              "email" -> JsString(value.email)
            ))
        }
      // etc...
    }
    

    到现在为止,一直都还不错!然后我可以这样使用:

    import JsonWriterInstances._
    Json.toJson(Person("Dave", "dave@example.com"))
    

    稍后,我遇到了一种称为接口语法的东西,它使用扩展方法通过如下接口方法扩展现有类型:

    object JsonSyntax {
      implicit class JsonWriterOps[A](value: A) {
        def toJson(implicit w: JsonWriter[A]): Json =
          w.write(value)
      }
    }
    

    这样就简化了将人员序列化为以下内容的调用:

    import JsonWriterInstances._
    import JsonSyntax._
    Person("Dave", "dave@example.com").toJson
    

    我不明白的是,这个人是如何装箱到JsonWriterOps中的,这样我就可以直接调用toJson,就像toJson是在Person case类本身中定义的一样。我喜欢这种魔力,但我无法理解关于JsonWriterOps的最后一步。那么,这种接口语法背后的想法是什么?它是如何工作的?有什么帮助吗?

    1 回复  |  直到 8 年前
        1
  •  5
  •   mpetruska    8 年前

    这实际上是一个标准的Scala功能,因为 JsonWriterOps 已标记 implicit 并且在范围内,编译器可以在需要时在编译时应用它。 因此,scalac将执行以下转换:

    Person("Dave", "dave@example.com").toJson
    new JsonWriterOps(Person("Dave", "dave@example.com")).toJson
    new JsonWriterOps[Person](Person("Dave", "dave@example.com")).toJson
    

    旁注 :

    将隐式类作为如下值类更有效:

    implicit class JsonWriterOps[A](value: A) extends AnyVal
    

    这使得编译器也优化了新的对象构造,如果可能的话,将整个隐式转换+方法调用编译为一个简单的函数调用。