首页 > 解决方案 > 与 Scala 隐式类型不匹配

问题描述

我正在尝试在 Scala 中使用隐式。

object TypeClasses extends App {
  trait HTMLWritable {
    def toHTML: String
  }

  case class User(name: String, age: Int, email: String) extends HTMLWritable {
    override def toHTML: String = s"<div>$name ($age yo) <a href=$email/> </div>"
  }

  val john = User("John", 32, "john@rockthejvm.com")
  trait HTMLSerializer[T] {
    def serialize(value: T): String
  }

  object UserSerializer extends HTMLSerializer[User] {
    def serialize(user: User): String = s"<div>${user.name} (${user.age} yo) <a href=${user.email}/> </div>"
  }

  implicit class HTMLEnrichment[T](value: T) {
    def toHTML(serializer: HTMLSerializer[T]): String = serializer.serialize(value)
  }

  println(john.toHTML(UserSerializer))
}

此代码不会编译:

Error:(41, 23) type mismatch;
 found   : lectures.part4implicits.TypeClasses.UserSerializer.type
 required: Int
  println(john.toHTML(UserSerializer))

我无法理解该消息,因为根据 IntelliJ,是对类上的方法john.toHTML的调用,它需要 a ,这是我给它的。我没有在任何地方定义过需要.toHTMLHTMLEnrichmentHTMLSerializertoHTMLInt

标签: scalaimplicit

解决方案


这是因为您不小心重载了该toHTML方法。您收到的错误是因为String.apply返回给定索引处的字符,这就是您收到有关Int.

Intelij 在识别这种阴影方面并不总是有效的。将隐式机制远离域模型是一个好主意,例如将专门的序列化解耦为隐式,就像你正在做的那样:

  implicit object UserSerializer extends HTMLSerializer[User] {
    def serialize(user: User): String = s"<div>${user.name} (${user.age} yo) <a href=${user.email}/> </div>"
  }

然后从您的 中删除所有内容user,并可能添加一个助手。

trait HTMLSerializer {
  def toHTML: String
}

object HTMLSerializer {
  // if you put this here you don't need to explicitly import it.
  implicit class HTMLEnrichment[T](val value: T) extends AnyVal {
    def toHTML(implicit serializer: HTMLSerializer[T]): String = 
      serializer.serialize(value)
  }
}

这意味着您可以简单地获得与使用伴随方法相同的效果,但您可以保持一切都很好地解耦,并且您不会冒着产生这些阴影效果的风险。


推荐阅读