0

我正在写剧本!2.1 应用使用ReactiveMongo。每个可持久的案例类都有一个包含 2 个隐式对象的对象,实现 BSONReader[...] 和 BSONWriter[...],每个案例类都有返回这些的方法:

  trait Persistable {
    implicit def getReader: BSONReader[Persistable]
    implicit def getWriter: BSONWriter[Persistable]
    val collectionName: String
  }

  case class MyObj () extends Persistable {
    override val collectionName: String = MyObj.collectionName
    override def getReader: BSONReader[MyObj] = MyObj.MyObjBSONReader
    override def getWriter: BSONWriter[MyObj] = MyObj.MyObjBSONWriter
  }

  object MyObj{
    val collectionName: String = "MyObj"

    implicit object MyObjBSONReader extends BSONReader[MyObj] {
      def fromBSON(document: BSONDocument): MyObj = {
        val doc = document.toTraversable
        new MyObj(
        )
      }
    }

    implicit object MyObjBSONWriter extends BSONWriter[MyObj] {
      def toBSON(myObj: MyObj) = {
        BSONDocument(
        )
      }
    }

出于某种原因,getReader 似乎工作正常,但 getWriter 错误:

在类型的 trait Persistable 中覆盖方法 getWriter = reactivemongo.bson.handlers.BSONWriter[models.persistable.Persistable]; 方法 getWriter 具有不兼容的类型

我究竟做错了什么?两者似乎都有相似的签名。另一个提示是,如果我从 getWriter 中删除返回类型,我会在 eclipse 中得到编译时间错误:

类型不匹配; 找到:models.persistable.MyObj.MyObjBSONWriter.type 需要:reactivemongo.bson.handlers.BSONWriter[models.persistable.Persistable]

更新

我按照@AndrzejDoyle 下面所说的做了,但是作为本练习的核心的 Persister 的实现抱怨:

def insert(persistable: Persistable) = {
    val collection = db(persistable.collectionName)
    import play.api.libs.concurrent.Execution.Implicits._

    implicit val reader = persistable.getReader
    implicit val writer = persistable.getWriter
    collection.insert(persistable)
}

错误:

trait Persistable 接受类型参数

4

1 回答 1

1

这是由于协变和逆变。

mongodb 阅读器定义为BSONReader[+DocumentType]. 泛型参数中的 + 表示该类在该参数中是协变的。或者更全面地说,

如果B是 的子类A,则BSONReader[B]是 的子类BSONReader[A]

因此,您可以在需要 a 的BSONReader[MyObj]任何地方使用 a BSONReader[Persistable]

另一方面,作者是逆变的:BSONWriter[-DocumentType]。这意味着

如果B是 的子类A,则BSONWriter[B]是 的BSONWriter[A]

因此 yourBSONWriter[MyObj]不是的子类,因此不能BSONWriter[Persistable]代替它使用。


这最初可能看起来令人困惑(即“为什么逆变在'向后'时才有意义?”)。但是,如果您考虑一下这些课程在做什么,它就会变得更加清晰。读者可能会生成其通用参数的一些实例。然后,调用者可能期望它产生Persistable- 如果您有一个专门产生MyObjs 的版本,那么这很好。

另一方面,作者可能被赋予了其通用参数的对象。带有 a 的调用者BSONWriter[Persistable]将调用该方法,并传入一个要写入write()的实例。Persistable您的实现只能编写 的实例MyObj,因此它实际上与接口不匹配。另一方面,aBSONWriter[Object]将是任何 BSONWriter 的子类,因为它可以(从类型的角度)接受任何类型作为参数。


根本问题似乎是您的Persistable特质比您预期的要宽松。您可能希望每个实现都返回一个以其自身参数化的读取器和写入器,而不是Persistable一般地。您可以通过自界泛型来实现这一点:

trait Persistable[T <: Persistable[T]] {
    implicit def getReader: BSONReader[T]
    implicit def getWriter: BSONWriter[T]
    val collectionName: String
}

然后将类声明为MyObj[MyObj]. 现在 reader 和 writer 预计将在 上进行参数化MyObj,并且您现有的实现将编译。

于 2013-04-26T08:00:48.993 回答