为什么这个getOrElse语句返回类型ANY?

vex*_*ace 4 json scala optional

我正在尝试按照教程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()
    }
  }

}
Run Code Online (Sandbox Code Playgroud)

和我的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)
    ))

  }
}
Run Code Online (Sandbox Code Playgroud)

为了完整性,我的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")

  }
Run Code Online (Sandbox Code Playgroud)

和路线

 # 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)
Run Code Online (Sandbox Code Playgroud)

当我尝试运行我的项目时,我收到以下错误:

编译错误

现在bar.id被定义为一个Option [Long],所以bar.id.getOrElse(0L)应该返回一个Long,据我所知,但它显然返回了Any.谁能帮我理解为什么?

谢谢!

yǝs*_*ǝla 7

这就是Scala中类型推断的工作方式......

首先,隐式转换IntBigDecimal:

scala> (1 : Int) : BigDecimal
res0: BigDecimal = 1
Run Code Online (Sandbox Code Playgroud)

该转换允许Int在构造选项之前进行转换:

scala> Some(1) : Option[BigDecimal]
res1: Option[BigDecimal] = Some(1)
Run Code Online (Sandbox Code Playgroud)

如果我们尝试getOrElse自己的类型可以修复,我们得到预期的类型Int:

scala> Some(1).getOrElse(2)
res2: Int = 1
Run Code Online (Sandbox Code Playgroud)

但是,这不起作用(你遇到的问题):

scala> Some(1).getOrElse(2) : BigDecimal
<console>:11: error: type mismatch;
 found   : Any
 required: BigDecimal
       Some(1).getOrElse(2) : BigDecimal
                        ^
Run Code Online (Sandbox Code Playgroud)

在执行类型推断之后,Scala的隐式转换最后一次启动.这是有道理的,因为如果你不知道类型,你怎么知道需要应用什么转换.Scala可以看到这BigDecimal是预期的,但它有一个Int基于它的类型的结果Option.所以它试图扩大型,无法找到任何匹配BigDecimalInt的类型层次和失败,出现错误.

这是有效的,但是因为类型在变量声明中是固定的:

scala> val v = Some(1).getOrElse(2)
v: Int = 1

scala> v: BigDecimal
res4: BigDecimal = 1
Run Code Online (Sandbox Code Playgroud)

所以我们需要以某种方式帮助编译器 - 任何类型的注释或显式转换都可以工作.选择你喜欢的任何一个:

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
Run Code Online (Sandbox Code Playgroud)