Создайте сопутствующий объект, который смешивается в признаке, определяющем метод, который возвращает объект класса сопутствующего объекта.
Абстрактная проблема: Создайте признак, который можно смешать с сопутствующим объектом класса, чтобы дать этому объекту метод, который возвращает объект этого класса.
Конкретная проблема: я пытаюсь создать группу классов для использования с вызовами службы 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 разрешила спецификацию наследования и ограничений типа-параметра на основе отношений класс / признак и сопутствующий объект, при условии, что он уже в некоторой степени осведомлен о модификаторах доступа и неявных областях.