【问题标题】:Can i pass different form data types using covariance in scala?我可以在scala中使用协方差传递不同的表单数据类型吗?
【发布时间】:2018-06-05 13:03:13
【问题描述】:

documentation for covariance 声明的方式相同:

对于某些类 List[+A],使 A 协变意味着对于两种类型 A 和 B 其中 A 是 B 的子类型,则 List[A] 是 列表[B]

这也可以应用于Form(如 scala 形式)?我在传递各种类型的表格时遇到问题。这是我正在做的一个示例:

   abstract class SuperCovariance

   case class TestCovariance1(id: Option[Int], name: String, another: String) extends SuperCovariance

   case class TestCovariance2(id: Option[Int], differentName: String, other: String) extends SuperCovariance

   object Tc1Form {

     val form = Form(
       mapping(
         "_id" -> optional(number),
         "name" -> nonEmptyText,
         "another" -> nonEmptyText
       )(TestCovariance1.apply)(TestCovariance1.unapply)
     )

   }

   object Tc2Form {

     val form = Form(
       mapping(
         "_id" -> optional(number),
         "differentName" -> nonEmptyText,
         "other" -> nonEmptyText
       )(TestCovariance2.apply)(TestCovariance2.unapply)
     )

   }

如果其中一种形式可以作为参数传递给我认为可以这样做的方法:

     def acceptAnySubTypeOfSuperCovariance(myForm: Form[SuperCovariance])(implicit request: Request[AnyContent]): Result = {
       Ok(views.html.myTemplate(myForm))
     }

调用此方法并传递Form[TestCovariance1] 类型会导致错误,这导致我在理解以及如何避免为SuperCovariance 的每个子类型编写此类方法方面遇到问题。欢迎任何帮助/启发!


澄清我希望能够做到这一点的原因 - 这是为了避免为每个表单编写重复的代码行。因此,如果我有多种形式,我希望在 controller 中使用一种方法来绑定来自请求的数据,例如

   def updateDoc(collectionName: String, oId: Option[BSONObjectID]) = Action.async { implicit request: Request[AnyContent] =>

       MyModel.form.bindFromRequest.fold(
         errorForm => Future.successful(Ok(views.html.errorTemplate(errorForm, MyModel)),
         data => {

           val updatedDoc = ?? // map data to model here, e.g. TestCovariance1(None, data.name, data.another)

           // calls a method 
           updateMultipleFields[MyModel](collectionName, updatedDoc, oId).map(x =>
             Redirect(routes.DatabaseC.editIndex(collName, oId.get))
           )

         }
       )
   }

  // generic method to update fields in collection (Mongo database)
  def updateMultipleFields[T](collectionName: String, model: T, id: Option[BSONObjectID])(implicit writes: Writer[T]): Future[Result] = {

    val idSelector = Json.obj("_id" -> id)
    getCollection(collectionName).flatMap(collection =>
      collection.update(idSelector, Json.obj("$set" -> model)).map {
        lastError =>
          Created("fields updated")
      }
    )

  }

我认为 MVC 结构会很灵活,允许我以正确(且安全)的方式设置数据处理;我可以通过创建新模型(表单和模型)来简单地添加数据。如果我不能;它不会阻止应用程序的功能,但似乎重复这些类似 样板 的代码是 Scala 存在的原因之一。

【问题讨论】:

  • 抛出的错误是什么?我认为在这种情况下你不需要强制协方差。
  • @nicodp 错误是 - *类型不匹配,预期:Form[SuperCovariance],预期:Form[TestCovariance1] *
  • 抱歉 - 错字:*类型不匹配,预期:Form[SuperCovariance],实际:Form[TestCovariance1] *

标签: forms scala types playframework covariance


【解决方案1】:

不,我不认为您提到的链接可以在您的示例中使用。为此,Form 类本身应该在T Form[+T] 中声明为协变,但他们不能这样做,因为它用于双向转换。你仍然可以通过

做你想做的事情
 def acceptAnySubTypeOfSuperCovariance[T:<SuperCovariance](myForm: Form[T])(implicit request: Request[AnyContent]): Result = 

但是这段代码让我感到疑惑:你真的想传递Form[some subclass of SuperCovariance]吗?你的acceptAnySubTypeOfSuperCovariance 到底做了什么你想做的事?通常,从请求中绑定数据并获取对象然后传递它更有意义。这将在不需要泛型的情况下工作。

【讨论】:

  • 感谢您的回答(很抱歉延迟回复)。我已经在上面编辑了我原来的问题,所以希望这会让我更清楚我为什么要这样做。
猜你喜欢
  • 1970-01-01
  • 2014-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-09
  • 1970-01-01
相关资源
最近更新 更多