2016-02-29 3 views
0

Я немного новичок в Scala, и я пытаюсь написать общий клиент для RESTful api, который я бы хотел использовать. Я могу предоставить конкретные Reads[T] и Writes[T] для конкретных классов случаев, для которых я хотел бы создать экземпляр моего клиента, однако компилятор рассчитывает найти Reads[T] и Writes[T] для любого типа, а не только для типов, которые я использую. Часть кода для иллюстрации (я опустил несоответствующие разделы):Общий клиент REST в Scala

Мой общий клиент:

class RestModule[T](resource: String, config: Config) ... with JsonSupport{ 
... 
def create(item: T): Future[T] = { 
    val appId = config.apiId 
    val path = f"/$apiVersion%s/applications/$appId%s/$resource" 

    Future { 
     val itemJson = Json.toJson(item) 
     itemJson.toString.getBytes 
    } flatMap { 
     post(path, _) 
    } flatMap { response => 
     val status = response.status 
     val contentType = response.entity.contentType 

     status match { 
     case Created => contentType match { 
      case ContentTypes.`application/json` => { 
      Unmarshal(response.entity).to[T] 
      } 
      case _ => Future.failed(new IOException(f"Wrong Content Type: $contentType")) 
     } 
     case _ => Future.failed(new IOException(f"HTTP Error: $status")) 
     } 
    } 
... 
} 

JsonSupprt Черта:

trait JsonSupport { 
    implicit val accountFormat = Json.format[Account] 
} 

Я только когда-либо инстанцировании, как RestModule[Account]("accounts",config), но я получаю погрешность

Error:(36, 32) No Json serializer found for type T. Try to implement an implicit Writes or Format for this type. 
    val itemJson = Json.toJson(item) 
         ^

Почему компилятор считает, что для типа T нужны буквы W, если T может только быть типа Account? Есть ли способ обойти это?

ответ

1

Причина, по которой компилятору не нравится то, что вы делаете, связано с тем, как параметры implicit разрешены и, что более важно, , когда разрешены.

Рассмотрим фрагмент кода,

Object MyFunc { 
    def test()(implicit s: String): String = s 
} 

Параметр implicit только получает разрешен параметром при вызове функции, и в основном расширена,

MyFunc.test()(resolvedImplicit) 

В вашем конкретном случае, если вы на самом деле назвать функция, требующая implicit и, следовательно, она ищет implicitT в этот момент времени. Поскольку он не может найти один из них, он не скомпилируется.

Для того, чтобы решить эту проблему, просто добавьте параметр implicit к методу create, чтобы сообщить компилятору, чтобы решить, когда вы звоните create, а не toJson в create.

Кроме того, мы можем использовать неявные правила Scala для получения желаемого поведения.

Давайте ваши черты Reads,

trait Reads[A] { 
} 

object MyFunc { 
    def create[A](a: A)(implicit reads: Reads[A]): Unit = ??? 
} 

как мы уже говорили befeore можно назвать, если implicit находится в области видимости. Однако, в данном конкретном случае, когда вы предопределенный читаете мы действительно можем поместить его в объекте компаньона,

object Reads { 
    implicit val readsInt: Reads[Int] = ??? 
    implicit val readsString: Reads[String] = ??? 
} 

Таким образом, когда create называется, пользователю не нужен импортировать или определить любые implicitvals когда A это Int или String, потому что scala автоматически ищет в сопутствующем объекте любые неявные определения, если он не может найти его в текущей области.

+0

Спасибо! То, что вы говорите, работает, но есть ли способ избежать изменения интерфейса или заставить вызывающего быть ответственным за предоставление неявных '' 'Reads''' и' '' Writes'''?Это то, что я собирался сделать, поставив их в черту, в идеале я хотел бы сделать их доступными неявно где-то в классе, без звонящих, которые должны беспокоиться о том, чтобы поместить их в область видимости. – Rag

+0

Конечно. Вы можете добавить параметр 'implicit', но добавьте свои' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '. Таким образом, когда кто-то использует 'create', scala сначала рассмотрит ваши предопределенные' Reads' и 'Writes'. – yw3410

+0

Отредактировал фрагмент, чтобы показать, что я имею в виду. – yw3410