隐含转换不适用

时间:2017-07-23 19:46:03

标签: scala implicit-conversion implicit

我试图研究将任意值转换为Json表示并且在未定义转换的情况下编译时错误的经典示例。

到目前为止,我有,

  trait Json

  trait ConvertableToJson[A] {
    def toJson: Json
  }

  object Json {

    case class Str(str: String) extends Json

    case class Int(int : Int) extends Json

    case class StrToJson(s: String) extends ConvertableToJson[StrToJson] {
      override def toJson: Json = Str(s)
    }

  }

  implicit def str2Json(s: String): StrToJson = StrToJson(s)

  def toJson[A <: ConvertableToJson[A]](a: A) = a.toJson

  println(toJson("some string"))

我希望上面的代码能够像:

toJson("some string")无法在没有implicit def的情况下编译。因为String <: ConvertableToJson[String]是假的。

然后使用,implicit def并找到Str2Json

Str2Json <: ConvertableToJson[Str2Json]应该是真的。

然而,这并没有发生,编译器抱怨:

Error: Inferred type arguments [String] do not conform to method toJson's type parameter bounds [A <: scalaz.ConvertToJson.ConvertableToJson[A]]
  println(toJson("dhruv"))
          ^ 

如果有人可以帮助我纠正我的理解,那就太棒了

1 个答案:

答案 0 :(得分:2)

因此您的代码存在两个问题。首先String不会扩展ConvertableToJson[String],这是您上一次函数调用所要做的。

第二个case class StrToJson应延长ConvertableToJson[String]而不是ConvertableToJson[StrToJson]

然后使用视图边界<%编译代码(参见下面的工作示例)。然而,这是一个坏主意,因为视图边界已被弃用为语言功能,您应该使用类型类。

trait Json

trait ConvertableToJson[A] {
  def toJson: Json
}

object Json {

  case class Str(str: String) extends Json

  case class Int(int : Int) extends Json

  case class StrToJson(s: String) extends ConvertableToJson[String] {
    override def toJson: Json = Str(s)
  }

}

import Json._

implicit def str2Json(s: String): StrToJson = StrToJson(s)

def toJson[A <% ConvertableToJson[A]](a: A) = a.toJson

println(toJson("some string"))

使用类型类

trait Json

trait ConvertableToJson[A] {
  // NOTE: this now takes a parameter
  def toJson(a: A): Json
}

object Json {

  case class Str(str: String) extends Json

  case class Int(int : Int) extends Json
}

import Json._

// NOTE: Because toJson takes a parameter the implicit implementation can now be an object
implicit object Str2Json extends ConvertableToJson[String] {
  override def toJson(a: String): Json = Str(a)
}

// NOTE: If you want to support the a.toJson syntax this implicit class adds it for all types with an implicit ConvertableToJson
implicit class ConvertableToJsonSyntax[A](a: A)(implicit ev: ConvertableToJson[A]) {
  def toJson: Json = ev.toJson(a)
}

// NOTE: Now we use context-bounds instead of view-bounds
def toJson[A : ConvertableToJson](a: A) = a.toJson

// NOTE: we can expand the context-bounds
def toJson2[A](a: A)(implicit ev: ConvertableToJson[A]) = a.toJson

// NOTE: But since we have the type class instance now, we do not need the extra syntax
def toJson3[A](a: A)(implicit ev: ConvertableToJson[A]) = ev.toJson(a)

println(toJson("some string"))