【问题标题】:Type mismatch with Scala implicits与 Scala 隐式类型不匹配
【发布时间】:2019-04-22 06:05:26
【问题描述】:

我正在尝试在 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 是对 HTMLEnrichment 类上的 toHTML 方法的调用,它需要 HTMLSerializer,这是我给它的.我没有在任何地方定义需要InttoHTML 方法。

【问题讨论】:

  • 问题是因为你的User 已经有一个toHTML 方法,它将使用它,而不是扩展方法。而且,由于该方法不带参数,编译器将其解释为在返回 toHTML 时调用 apply 方法,这是一个 String,其 apply 方法按索引返回 Character。这就是为什么编译器会警告Int - 尝试使用另一个名称(例如toHTML2 或删除正常的toHTML,或创建另一个User 类,等等。
  • 顺便说一句,通常将 隐式类value class 结合使用以避免实例化。此外,通常serializer 是隐式参数,UserSerializer 也是隐式参数。因为人们通常不想明确地传递它。 - 此外,案例类应该是最终的。而且,一般来说,对于像这样的简单用例,最好使用 implicit vals (with explicit type signature) 而不是对象。 implicit val UserSerializer: HTMLSerializer[User] = new HTMLSerializer[User] { ... }.

标签: scala implicit


【解决方案1】:

这是因为您不小心重载了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)
  }
}

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

【讨论】:

  • 首先,UserSerializer 必须是隐式的。其次,value 应该是一个 val 以使其成为值类 (现在它甚至无法编译)。另外,HTMLWritable trait 的目的是什么?
  • @LuisMiguelMejíaSuárez 说得对,感谢您发现了一些东西,我匆忙复制粘贴了。
猜你喜欢
  • 1970-01-01
  • 2015-04-09
  • 1970-01-01
  • 2016-10-21
  • 1970-01-01
  • 2014-07-01
  • 2020-08-09
  • 1970-01-01
  • 2012-08-27
相关资源
最近更新 更多