简体   繁体   English

使用Scala案例类建模

[英]Modeling with Scala case class

I'm attempting to model responses from REST APIs as case classes which I can use pattern matching on. 我正在尝试将REST API的响应建模为案例类,我可以使用模式匹配。

I thought it would be a good fit assuming inheritance, but I see that this is deprecated. 我认为这是一个很好的假设继承,但我看到这是不赞成的。 I know there are already questions related to case classes and inheritance, but my question is more about how you would model the following the "right way" here without inheritance. 我知道已经存在与案例类和继承相关的问题,但我的问题更多的是关于如何在没有继承的情况下在这里建模“正确的方法”。

I started with the following two case classes, which work fine: 我从以下两个案例类开始,它们工作正常:

case class Body(contentType: String, content: String)
case class Response(statusCode: Int, body: Body)

ie a REST call would return with something like: 即REST调用将返回类似于:

Response(200, Body("application/json", """{ "foo": "bar" }"""))

which I could pattern match like: 我可以模仿匹配,如:

response match {
  case Response(200, Body("application/json", json)) => println(json)
  case Response(200, Body("text/xml", xml)) => println(xml)
  case Response(_,_) => println("Something unexpected")
}

etc. which works fine. 等工作正常。

Where I ran into trouble is: I'd like helper extensions for these case classes, such as: 我遇到麻烦的地方是:我想要这些案例类的帮助扩展,例如:

case class OK(body: Body) extends Response(200, body)
case class NotFound() extends Response(404, Body("text/plain", "Not Found"))

case class JSON(json: String) extends Body("application/json", json)
case class XML(xml: String) extends Body("text/xml", xml)

so that I can do simplified pattern matches like this: 所以我可以做这样的简化模式匹配:

response match {
  case OK(JSON(json)) => println(json)
  case OK(XML(xml)) => println(xml)
  case NotFound() => println("Something is not there")

  // And still drop down to this if necessary:
  case Response(302, _) => println("It moved")
}

and also which would also allow my REST code to directly use and return: 并且还允许我的REST代码直接使用和返回:

Response(code, Body(contentType, content))

which is easier to build a response dynamically. 这更容易动态地构建响应。

so... 所以...

I can get it to compile (with deprecation warnings) via: 我可以通过以下方式编译(使用弃用警告):

case class OK(override val body: Body) extends Response(200, body)

However, this doesn't seem to work with pattern matching. 但是,这似乎不适用于模式匹配。

Response(200, Body("application/json", "")) match {
  case OK(_) => ":-)"
  case _ => ":-("
}
res0: java.lang.String = :-(

Any ideas on how this could work? 关于这如何工作的任何想法? I'm open to different approaches, but this was my attempt to find a practical use for case classes 我对不同的方法持开放态度,但这是我试图找到案例类的实际用途

There are several reasons why case classes shouldn't be subclassed . 案例类不应该被子类化有几个原因。 In your case, the problem becomes that OK is another type than (a subtype of) Response , therefore the match fails (even if the arguments match, the type doesn't match). 在您的情况下,问题变为OK是( Response一个子类型)的另一种类型,因此匹配失败(即使参数匹配,类型也不匹配)。

You will want custom extractors , instead. 您将需要自定义提取器 For example: 例如:

case class Response(code: Int, body: String)
object OK {
  def apply(body: String) = Response(200, body)
  def unapply(m: Response): Option[String] = m match {
    case Response(200, body) => Some(body)
    case _                   => None
  }
}

def test(m: Response): String = m match {
   case OK(_) => ":-)"
   case _     => ":-("
}

test(Response(300, "Hallo"))  // :-(
test(Response(200, "Welt"))   // :-)
test(OK("Welt"))              // :-)

There are few more examples of custom extractors in this thread . 此线程中有更多自定义提取器的示例。

Have you looked at the scala library unfiltered? 你有未经过滤的scala库吗? http://unfiltered.lessis.me/ It may help you with approaching you problem . http://unfiltered.lessis.me/它可以帮助您解决问题。 HTH HTH

While custom extractors mentioned by 0__ can certainly be used, you'll lose exhaustiveness guarantees of sealed type hierarchies. 虽然0__提到的自定义提取器当然可以使用,但您将失去密封类型层次结构的详尽保证。 While in the example you gave in the question there's nothing sealed , the problem is well suited to them. 虽然你在问题中给出的例子没有任何sealed ,但问题非常适合他们。

In that case, my suggestion is to simply make sure the case class is always at the bottom of the type hierarchy, and make the upper classes normal. 在这种情况下,我的建议是简单地确保case class始终位于类型层次结构的底部,并使上层类正常。 For example: 例如:

sealed class Response(val statusCode: Int, val body: Body) sealed
case class Ok(override val body: Body) extends Response(200, body)
sealed class NotOk(statusCode: Int, body: Body) extends Response(statusCode, body)
case object NotFound extends NotOk(404, "Not found")
// and so on...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM