Scala 类型类模式和泛型方法

Posted

技术标签:

【中文标题】Scala 类型类模式和泛型方法【英文标题】:Scala type class pattern and generic methods 【发布时间】:2012-11-01 21:48:38 【问题描述】:

我正在尝试编写一个通用提取器,用于使用 spray 和 spray-json 解析 json POST 正文。

但是,我很难让它与多个模型一起使用。这是服务对象中的 case 语句:

import MyJsonProtocol._

...

def receive = 
  case Post (Routes.person.post, p: Person) => sender ! Ok(Actions.person.post(p))
  case Get  (Routes.foo.forId(x))           => sender ! Ok(x)
  case _                                    => sender ! Ok("No handler")

这是我写的提取器(只要在 case 语句的范围内只有一个模型的 JsonReader 就可以工作):

//NB. Json.parse returns an Option[T]
object Post extends Request 
  def unapply[T:JsonReader](req: HttpRequest): Option[(String, T)] = req match 
    case HttpRequest(POST, url, _, HttpBody(_, body), _) => Json.parse[T](body.asString).map((url, _))
    case _ => None
  

但是,一旦我添加了一个新模型(和关联的 JsonReader),代码就不再编译并出现此错误:

ambiguous implicit values:
[error]  both value personFormat in object Json of type => spray.json.RootJsonFormat[com.rsslldnphy.foam.models.Person]
[error]  and value animalFormat in object Json of type => spray.json.RootJsonFormat[com.rsslldnphy.foam.models.Animal]
[error]  match expected type spray.json.JsonReader[T]
[error]     case Post (Routes.person.post, p: Person) => sender ! Ok(Actions.person.post(p))

JsonReader 的泛型类型不同的事实似乎已丢失。这种类型的擦除吗?有没有办法得到我想要的?

这是迄今为止项目的完整编译代码,ExampleService 中的注释解释了导致它崩溃的原因:github.com/rsslldnphy/foam。感谢您的帮助,谢谢。

或者如果我想要的目前无法实现,任何人都可以提出替代方法吗?

【问题讨论】:

你试过case Post[Person]吗? 我得到:not found: type Post。我没有定义一个类,只是一个对象。 也许如果你想在答案中提供可编译的代码,你应该在问题中提供相同的内容 我不是在要求可编译的代码,只是解释你的意思以及为什么它应该工作!但如果有帮助,我会将整个项目上传到 github。 @KimStebel 我已经为问题添加了 github 链接。感谢您抽出宝贵时间。 【参考方案1】:

你需要给编译器明确的指令来完成这项工作。正如您在下面看到的,编译器无法推断 T 应该是什么。编译器需要能够从请求中动态查看 Json,并从中暗示一个类型(我们只能梦想它这样做;))

def unapply[T:JsonReader](req: HttpRequest): Option[(String, T)] = (...) Json.parse[T] (...)

这意味着你必须明确地注释帖子,如下所示:

import MyJsonProtocol._

...

def receive = 
  case Post[Person] (Routes.person.post, p: Person) => sender ! Ok(Actions.person.post(p))
  case Get  (Routes.foo.forId(x))           => sender ! Ok(x)
  case _                                    => sender ! Ok("No handler")

并将定义更改为此

case class Post[T: JsonReader] extends Request 
  def unapply(req: HttpRequest): Option[(String, T)] = req match 
    case HttpRequest(POST, url, _, HttpBody(_, body), _) => Json.parse[T](body.asString).map((url, _))
    case _ => None
  

【讨论】:

[error] /Users/russell.dunphy/Code/Personal/foam/src/main/scala/com/rsslldnphy/foam/ExampleService.scala:31: not found: type Post [error] case Post[Person] (Routes.person.post, p: Person) => sender ! Ok(Actions.person.post(p)) (另外,如果编译器不能从我明确地用p: Person 命名它的事实推断出 T 的类型吗?或者从它传递到的方法这一事实,@987654326 @,明确需要 Person 作为其参数?) 不,scala 推理不是那么强大,也不会从你返回的内容中推断出来。 unapply 也是一个奇怪的案例——花一秒钟研究如何使 unapply 参数化。 谢谢,但仍然无法编译:[error] /Users/russell.dunphy/Code/Personal/foam/src/main/scala/com/rsslldnphy/foam/ExampleService.scala:31: class Post of type com.rsslldnphy.foam.routing.Post does not take type parameters. [error] case Post[Person] (Routes.person.post, p: Person) => sender ! Ok(Actions.person.post(p)) 抱歉下载了 github 代码,将开始编译。五分钟

以上是关于Scala 类型类模式和泛型方法的主要内容,如果未能解决你的问题,请参考以下文章

JAVA——泛型类和泛型方法(静态方法泛型)

大数据学习之Scala语言的高级特性42

JavaSE习题 继承接口和泛型

Java泛型:泛型类泛型接口和泛型方法

反射和泛型的复习

Java泛型:泛型类泛型接口和泛型方法