抽象问题:创建一个可以混合到类的伴随对象中的特征,为该对象提供一个返回该类对象的方法。
具体问题:我试图创建一堆用于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
。但必须有一种方法可以在特质中定义它,并且只是混合这种特性。正确?
答案 0 :(得分:0)
解决方案是简化特征的类型参数。
trait DeserializeFromJson[T] {
def fromJson( json : String )(implicit m : Manifest[T]) : T =
JSON.parser.fromJson[T](json)(m)
}
现在,伴随对象可以扩展DeserializeFromJson[Foo]
,当我调用Foo.fromJson( json )
时,它可以告诉Genson正确的类型信息,以便创建相应类型的对象。
答案 1 :(得分:0)
问题与隐含的工作方式有关。 Genson期望一个Manifest,它将用它来知道它必须反序列化的类型。此清单在Genson中被定义为隐式,这意味着它将尝试从“调用者代码”中的隐式可用清单中获取它。但是在原始版本中,DeserializeFromJson中没有Manifest [T]。
另一种方法是像这样定义DeserializeFromJson(它只会产生一个带有隐式Manifest [T]参数的构造函数):
abstract class DeserializeFromJson[T: Manifest] {
def fromJson( json : String ) : T = JSON.parser.fromJson[T](json)
}
object Foo extends DeserializeFromJson[Foo]
更一般地说,如果你没有通过封装lib(在本例中为Genson)带来更多价值,我认为你不应该这样做。因为你基本上减少了Genson的功能(现在人们只能使用字符串)并引入你遇到的问题。
答案 2 :(得分:0)
我认为你的类型参数约束最初是错误的; 你有
trait DeserializeFromJson[T <: DeserializeFromJson[T]]
有了你自己的答案,你就完全放松了;你需要
trait DeserializeFromJson[T <: SerializeToJson]
...错误试图告诉你。
对隐式清单(现在我相信是 ClassTag)或上下文边界的需求是在金钱上。
如果 Scala 允许基于类/特征和伴生对象关系的继承和类型参数约束规范,这会很好,因为它在某种程度上已经意识到访问修饰符和隐式作用域。