Scala函数返回类型基于泛型

时间:2016-04-08 12:12:00

标签: scala generics playframework playframework-2.0

使用Scala泛型我试图在Play应用程序中抽象一些常用函数。函数返回Seq s,其中包含从REST JSON服务反序列化的对象。

def getPeople(cityName: String): Future[Seq[People]] = {
    getByEndpoint[People](s"http://localhost/person/$cityName")
}

def getPeople(): Future[Seq[Dog]] = {
    getByEndpoint[Dog]("http://localhost/doge")
}

使用泛型将获取和反序列化逻辑打包到单个函数中。

private def getByEndpoint[T](endpoint: String): Future[Seq[T]] = {

    ws.url(endpoint)
      .get()
      .map(rsp => rsp.json)
      .flatMap { json =>
        json.validate[Seq[T]] match {
          case s: JsSuccess[Seq[T]] =>
            Future.successful(s.get)
          case e: JsError =>
            Future.failed(new RuntimeException(s"Get by endpoint JSON match failed: $e"))
        }
      }

}

问题是我得到了“No Json deserializer found for type Seq[T]. Try to implement an implicit Reads or Format for this type.”。我确定我没有在T中正确使用Seq[T](至少根据我的C#/ Java存储器),但我找不到任何线索如何在Scala中正确使用它。一切都按预期工作,不使用泛型。

1 个答案:

答案 0 :(得分:2)

播放JSON使用type classes来捕获有关哪些类型可以(取消)与JSON序列化以及如何进行序列化的信息。如果您在范围内具有类型为Format[Foo]的隐式值,则称其为Format的{​​{1}}类型类的实例。

这种方法的优点在于它为我们提供了一种约束泛型类型(并在编译时检查这些约束)的方法,这种方法并不依赖于子类型。例如,标准库Foo无法扩展Play(或任何其他库)可能提供的某种String特性,因此我们需要某种方式说"我们知道如何将Jsonable编码为JSON"这并不会使String成为我们自己定义的某种特质的子类型。

在Play JSON中,您可以通过定义隐式String实例来实现此目的,而Play本身为您提供了许多这些实例(例如,如果您为Format提供了一个,那么&#39 ;给你一个T)。 Seq[T]上的validate方法需要其中一个实例(实际上是JsValueFormat的子类型,但这里的类型参数不是非常相关) - 在这种情况下为Reads - 除非编译器能够找到该实例,否则它不会编译。

您可以通过将约束添加到您自己的通用方法来提供此实例:

Seq[T]

现在使用private def getByEndpoint[T: Format](endpoint: String): Future[Seq[T]] = { ... } 语法,您已指定T: Format必须有Format个实例(即使您没有约束T以任何其他方式),因此编译器知道如何为T调用所需的Format提供Seq[T]实例。