创建一个混合在特征中的伴随对象,该特征定义了一个方法,该方法 returns 对象的伴随对象 class
Create a companion object that mixes in a trait that defines a method which returns an object of the object's companion class
抽象问题:创建一个可以混合到 class 的伴随对象中的特征,为该对象提供一个 returns class 对象的方法。
具体问题:我正在尝试创建一堆 classes 用于 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
执行 serialization/deserialization,我通过全局对象访问它:
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
因为只有 class 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
所以我可以尝试添加构造函数参数,然后编译和 运行s,但是当它尝试反序列化时 运行-time 类型又是错误的,给我上面的错误。
我唯一能做的就是在每个伴生对象中定义 fromJson
。但是必须有一种方法可以在特征中定义它,然后将其混合在该特征中。对吧?
解决方案是简化特征的类型参数。
trait DeserializeFromJson[T] {
def fromJson( json : String )(implicit m : Manifest[T]) : T =
JSON.parser.fromJson[T](json)(m)
}
现在,伴随对象可以扩展 DeserializeFromJson[Foo]
,当我调用 Foo.fromJson( json )
时,它可以告诉 Genson 正确的类型信息,以便创建适当类型的对象。
问题与隐含函数的工作方式有关。
Genson 需要一个 Manifest,它将使用它来了解它必须反序列化的类型。此清单在 Genson 中定义为隐式的,这意味着它将尝试从 "caller code" 中隐式可用的清单中获取它。但是,在您的原始版本中,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]
更一般地说,如果您不通过封装库(在本例中为 Genson)带来更多价值,我认为您不应该那样做。因为你基本上减少了 Genson 的功能(现在人们只能使用字符串)并引入了你遇到的问题。
我认为你的类型参数约束本来就是错误的;
你有
trait DeserializeFromJson[T <: DeserializeFromJson[T]]
有了自己的回答,你就彻底放松了;你需要
trait DeserializeFromJson[T <: SerializeToJson]
...错误试图告诉您的内容。
对隐式清单(我相信现在是 ClassTag)或上下文边界的需要是很重要的。
Scala 允许基于 class/trait 和伴随对象关系的继承和类型参数约束规范会很好,因为它在某种程度上已经知道访问修饰符和隐式范围。
抽象问题:创建一个可以混合到 class 的伴随对象中的特征,为该对象提供一个 returns class 对象的方法。
具体问题:我正在尝试创建一堆 classes 用于 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
执行 serialization/deserialization,我通过全局对象访问它:
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
因为只有 class 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
所以我可以尝试添加构造函数参数,然后编译和 运行s,但是当它尝试反序列化时 运行-time 类型又是错误的,给我上面的错误。
我唯一能做的就是在每个伴生对象中定义 fromJson
。但是必须有一种方法可以在特征中定义它,然后将其混合在该特征中。对吧?
解决方案是简化特征的类型参数。
trait DeserializeFromJson[T] {
def fromJson( json : String )(implicit m : Manifest[T]) : T =
JSON.parser.fromJson[T](json)(m)
}
现在,伴随对象可以扩展 DeserializeFromJson[Foo]
,当我调用 Foo.fromJson( json )
时,它可以告诉 Genson 正确的类型信息,以便创建适当类型的对象。
问题与隐含函数的工作方式有关。 Genson 需要一个 Manifest,它将使用它来了解它必须反序列化的类型。此清单在 Genson 中定义为隐式的,这意味着它将尝试从 "caller code" 中隐式可用的清单中获取它。但是,在您的原始版本中,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]
更一般地说,如果您不通过封装库(在本例中为 Genson)带来更多价值,我认为您不应该那样做。因为你基本上减少了 Genson 的功能(现在人们只能使用字符串)并引入了你遇到的问题。
我认为你的类型参数约束本来就是错误的; 你有
trait DeserializeFromJson[T <: DeserializeFromJson[T]]
有了自己的回答,你就彻底放松了;你需要
trait DeserializeFromJson[T <: SerializeToJson]
...错误试图告诉您的内容。
对隐式清单(我相信现在是 ClassTag)或上下文边界的需要是很重要的。
Scala 允许基于 class/trait 和伴随对象关系的继承和类型参数约束规范会很好,因为它在某种程度上已经知道访问修饰符和隐式范围。