Создайте сопутствующий объект, который смешивается в признаке, определяющем метод, который возвращает объект класса сопутствующего объекта.

Абстрактная проблема: Создайте признак, который можно смешать с сопутствующим объектом класса, чтобы дать этому объекту метод, который возвращает объект этого класса.

Конкретная проблема: я пытаюсь создать группу классов для использования с вызовами службы RESTful, которые знают, как самим сериализовать и десериализовать, например так:

case class Foo
(
    var bar : String,
    var blip : String
)
extends SerializeToJson
object Foo extends DeserializeFromJson

Предполагаемое использование примерно так:

var f = Foo( "abc","123" )
var json = f.json
var newF = Foo.fromJson( json )

я использую Genson выполнить сериализацию / десериализацию, к которой я получаю доступ через глобальный объект:

object JSON {
  val parser = new ScalaGenson( new GensonBuilder() <...> )
}

Затем я определяю черты следующим образом:

trait SerializeToJson {
  def json : String = JSON.parser.toJson(this)
}
trait DeserializeFromJson[T <: DeserializeFromJson[T]] {
  def fromJson( json : String ) : T = JSON.parser.fromJson( json )
}

Это компилируется. Но это не так:

object Foo extends DeserializeFromJson[Foo]

Я получаю следующее сообщение об ошибке:

type arguments [Foo] do not conform to trait DeserializeFromJson's 
type parameter bounds [T <: DeserializeFromJson[T]] 

Я пытался создать одну черту, например, так:

trait JsonSerialization[T <: JsonSerialization[T]] {

  def json(implicit m: Manifest[JsonSerialization[T]]) : String = 
    JSON.parser.toJson(this)(m)

  def fromJson( json : String ) : T = 
    JSON.parser.fromJson(json)

}

Теперь, если я просто объявлю case class Foo (...) extends JsonSerialization[Foo] тогда я не могу позвонить Foo.fromJson потому что только экземпляр класса Foo имеет этот метод, а не объект-компаньон.

Если я заявляю object Foo extend JsonSerialization[Foo] тогда я могу скомпилировать и Foo имеет .fromJson метод. Но во время выполнения, вызов fromJson считает, что T это JsonSerializationи не Fooили так следующая ошибка во время выполнения предполагает:

java.lang.ClassCastException: scala.collection.immutable.HashMap$HashTrieMap cannot be cast to ...JsonSerialization
at ...JsonSerialization$class.fromJson(DataModel.scala:14)
at ...Foo.fromJson(Foo.scala:6)

И я не могу объявить object Foo extends Foo потому что я получаю

module extending its companion class cannot use default constructor arguments

Поэтому я могу попытаться добавить параметры конструктора, и он компилируется и запускается, но опять-таки тип времени выполнения, когда он пытается десериализоваться, неверен, что выдает мне вышеуказанную ошибку.

Единственное, что мне удалось сделать, это работает, это определить fromJson в каждом объекте-компаньоне. Но ДОЛЖЕН быть способ определить его по признаку и просто смешать его. Правильно?

3 ответа

Решение

Решение состоит в том, чтобы упростить параметр типа для признака.

trait DeserializeFromJson[T] { 
  def fromJson( json : String )(implicit m : Manifest[T]) : T = 
    JSON.parser.fromJson[T](json)(m)
}

Теперь объект-компаньон может расширяться DeserializeFromJson[Foo] и когда я звоню Foo.fromJson( json ) он может сообщить Генсону правильную информацию о типе, чтобы был создан объект соответствующего типа.

Проблема связана с тем, как это влияет. Дженсон ожидает Манифест, который он будет использовать, чтобы знать, к какому типу он должен десериализоваться. Этот манифест определен как неявный в Genson, что означает, что он попытается получить его из неявно доступных манифестов в "коде вызывающего". Однако в вашей оригинальной версии нет манифеста [T] в DeserializeFromJson.

Альтернативным способом было бы определить DeserializeFromJson следующим образом (который просто создаст конструктор с неявным аргументом Manifest[T]):

abstract class DeserializeFromJson[T: Manifest] {
  def fromJson( json : String ) : T = JSON.parser.fromJson[T](json)
}

object Foo extends DeserializeFromJson[Foo]

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

Я думаю, что изначально ваше ограничение параметра типа было неправильным; у вас

      trait DeserializeFromJson[T <: DeserializeFromJson[T]]

Своим собственным ответом вы полностью расслабили его; тебе нужно было

      trait DeserializeFromJson[T <: SerializeToJson]

... что ошибка пыталась вам сказать.

Потребность в неявном манифесте (теперь я считаю, ClassTag) или контекстных границах была на деньги.

Было бы неплохо, если бы Scala разрешила спецификацию наследования и ограничений типа-параметра на основе отношений класс / признак и сопутствующий объект, при условии, что он уже в некоторой степени осведомлен о модификаторах доступа и неявных областях.

Другие вопросы по тегам