我正在尝试按照教程https://www.jamesward.com/2012/02/21/play-framework-2-with-scala-anorm-json-coffeescript-jquery-heroku进行操作,但是自从教程开始以来,play-scala已经发生了变化(就像我找到的每个教程一样)。我正在使用2.4.3这要求我真正了解事情是如何运作的,不一定是坏事。
给我带来麻烦的一件事是getOrElse方法。
这是我的Bar.scala模型
package models
import play.api.db._
import play.api.Play.current
import anorm._
import anorm.SqlParser._
case class Bar(id: Option[Long], name: String)
object Bar {
val simple = {
get[Option[Long]]("id") ~
get[String]("name") map {
case id~name => Bar(id, name)
}
}
def findAll(): Seq[Bar] = {
DB.withConnection { implicit connection =>
SQL("select * from bar").as(Bar.simple *)
}
}
def create(bar: Bar): Unit = {
DB.withConnection { implicit connection =>
SQL("insert into bar(name) values ({name})").on(
'name -> bar.name
).executeUpdate()
}
}
}
和我的BarFormat.scala Json格式化程序
package models
import play.api.libs.json._
import anorm._
package object Implicits {
implicit object BarFormat extends Format[Bar] {
def reads(json: JsValue):JsResult[Bar] = JsSuccess(Bar(
Option((json \ "id").as[Long]),
(json \ "name").as[String]
))
def writes(bar: Bar) = JsObject(Seq(
"id" -> JsNumber(bar.id.getOrElse(0L)),
"name" -> JsString(bar.name)
))
}
}
为了完整性,我的Application.scala控制器:
package controllers
import play.api.mvc._
import play.api.data._
import play.api.data.Forms._
import javax.inject.Inject
import javax.inject._
import play.api.i18n.{ I18nSupport, MessagesApi, Messages, Lang }
import play.api.libs.json._
import views._
import models.Bar
import models.Implicits._
class Application @Inject()(val messagesApi: MessagesApi) extends Controller with I18nSupport {
val barForm = Form(
single("name" -> nonEmptyText)
)
def index = Action {
Ok(views.html.index(barForm))
}
def addBar() = Action { implicit request =>
barForm.bindFromRequest.fold(
errors => BadRequest,
{
case (name) =>
Bar.create(Bar(None, name))
Redirect(routes.Application.index())
}
)
}
def listBars() = Action { implicit request =>
val bars = Bar.findAll()
val json = Json.toJson(bars)
Ok(json).as("application/json")
}
和路线
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# Home page
POST /addBar controllers.Application.addBar
GET / controllers.Application.index
GET /listBars controllers.Application.listBars
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
当我尝试运行我的项目时,我收到以下错误:
现在bar.id被定义为一个Option [Long]所以bar.id.getOrElse(0L)应该返回一个Long,据我所知,但它显然返回了Any。谁能帮我理解为什么?
谢谢!
答案 0 :(得分:7)
这就是Scala中类型推断的工作方式......
首先,存在从Int
到BigDecimal
的隐式转换:
scala> (1 : Int) : BigDecimal
res0: BigDecimal = 1
该转换允许在构造选项之前转换Int
:
scala> Some(1) : Option[BigDecimal]
res1: Option[BigDecimal] = Some(1)
如果我们自己尝试getOrElse
类型可以修复,我们会得到预期类型Int
:
scala> Some(1).getOrElse(2)
res2: Int = 1
然而,这不起作用(你遇到的问题):
scala> Some(1).getOrElse(2) : BigDecimal
<console>:11: error: type mismatch;
found : Any
required: BigDecimal
Some(1).getOrElse(2) : BigDecimal
^
在执行类型推断之后,Scala的隐式转换最后启动。这是有道理的,因为如果你不知道类型,你怎么知道需要应用什么转换。 Scala可以看到BigDecimal
是预期的,但它根据Int
的类型有Option
结果。因此它尝试扩展类型,找不到与BigDecimal
的类型层次结构中的Int
匹配的任何内容,并因错误而失败。
这样可行,但因为类型在变量声明中已修复:
scala> val v = Some(1).getOrElse(2)
v: Int = 1
scala> v: BigDecimal
res4: BigDecimal = 1
所以我们需要以某种方式帮助编译器 - 任何类型的注释或显式转换都可以工作。选择你喜欢的任何一个:
scala> (Some(1).getOrElse(2) : Int) : BigDecimal
res5: BigDecimal = 1
scala> Some(1).getOrElse[Int](2) : BigDecimal
res6: BigDecimal = 1
scala> BigDecimal(Some(1).getOrElse(2))
res7: scala.math.BigDecimal = 1
答案 1 :(得分:2)
以下是signature for Option.getOrElse
method:
getOrElse[B >: A](default: ⇒ B): B
术语B >: A
表示类型参数B
或抽象类型B
引用类型为A
的超类型,在本例中为Any
是Long
:
val l: Long = 10
val a: Any = l
因此,我们可以使用getOrElse
执行非常类似的操作:
val some: Option[Long] = Some(1)
val value: Any = option.getOrElse("potatos")
val none: Option[Long] = None
val elseValue: Any = none.getOrElse("potatos")
这将我们带到您的场景:getOrElse
返回的类型将是Any
而不是BigDecimal
,因此您需要另一种方法来处理这种情况,例如使用{ {1}},每个实例:
fold
其他一些可以帮助您的讨论: