简体   繁体   English

创建一个伴随对象,该对象混合了一个特性,该特性定义了一个方法,该方法返回对象的伴随类的对象

[英]Create a companion object that mixes in a trait that defines a method which returns an object of the object's companion class

Abstract problem: Create a trait that can be mixed into the companion object of a class, to give that object a method that returns an object of that class.抽象问题:创建一个可以混合到类的伴生对象中的特征,为该对象提供一个返回该类对象的方法。

Concrete problem: I'm trying to create a bunch of classes for use with RESTful service calls, that know how to serialize and de-serialize themselves, like so:具体问题:我正在尝试创建一堆用于 RESTful 服务调用的类,它们知道如何序列化和反序列化自己,如下所示:

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

The intended usage is like so:预期用途如下:

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

I'm using Genson to do the serialization/deserialization, which I access through a global object:我正在使用Genson进行序列化/反序列化,我通过全局对象访问它:

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

Then I define the traits like so:然后我像这样定义特征:

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

This compiles.这编译。 But this does not:但这不会:

object Foo extends DeserializeFromJson[Foo]

I get the following error message:我收到以下错误消息:

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

I've tried creating a single trait, like so:我尝试创建一个特性,如下所示:

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)

}

Now, if I just declare case class Foo (...) extends JsonSerialization[Foo] then I can't call Foo.fromJson because only an instance of class Foo has that method, not the companion object.现在,如果我只是声明case class Foo (...) extends JsonSerialization[Foo]那么我不能调用Foo.fromJson因为只有类Foo一个实例具有该方法,而不是伴随对象。

If I declare object Foo extend JsonSerialization[Foo] then I can compile and Foo has a .fromJson method.如果我声明object Foo extend JsonSerialization[Foo]那么我可以编译并且Foo有一个.fromJson方法。 But at run time, the call to fromJson thinks that T is a JsonSerialization , and not a Foo , or so the following run-time error suggests:但是在运行时,对fromJson的调用认为TJsonSerialization ,而不是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)

And I can't declare object Foo extends Foo because I get我不能声明object Foo extends Foo因为我得到

module extending its companion class cannot use default constructor arguments

So I can try adding constructor parameters, and that compiles and runs, but again the run-time type when it tries to deserialize is wrong, giving me the above error.所以我可以尝试添加构造函数参数,然后编译并运行,但是尝试反序列化时的运行时类型又是错误的,给了我上述错误。

The only thing I've been able to do that works is to define fromJson in every companion object.我唯一能做的就是在每个伴随对象中定义fromJson But there MUST be a way to define it in a trait, and just mix in that trait.但是必须有一种方法可以在特征中定义它,并且只需混合该特征。 Right?对吗?

The solution is to simplify the type parameter for the trait.解决方案是简化特征的类型参数。

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

Now, the companion object can extend DeserializeFromJson[Foo] and when I call Foo.fromJson( json ) it is able to tell Genson the correct type information so that an object of the appropriate type is created.现在,伴随对象可以扩展DeserializeFromJson[Foo]并且当我调用Foo.fromJson( json )它能够告诉 Genson 正确的类型信息,以便创建适当类型的对象。

The problem is related to how implicits work.问题与隐式的工作方式有关。 Genson expects a Manifest that it will use to know to what type it must deserialize. Genson 需要一个 Manifest,它将用来知道它必须反序列化为什么类型。 This manifest is defined as implicit in Genson, meaning that it will try to get it from implicitly available manifests in the "caller code".此清单在 Genson 中被定义为隐式,这意味着它将尝试从“调用方代码”中的隐式可用清单中获取它。 However in your original version there is no Manifest[T] in DeserializeFromJson.但是,在您的原始版本中,DeserializeFromJson 中没有 Manifest[T]。

An alternate way would be to define the DeserializeFromJson like that (which will just produce a constructor with an implicit Manifest[T] argument):另一种方法是像这样定义 DeserializeFromJson (它只会产生一个带有隐式 Manifest[T] 参数的构造函数):

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

object Foo extends DeserializeFromJson[Foo]

More generally if you don't bring more value by encapsulating a lib (in this case Genson), I think you shouldn't do that.更一般地说,如果您不通过封装 lib(在本例中为 Genson)来带来更多价值,我认为您不应该这样做。 As you basically reduce the features of Genson (now people can only work with strings) and introduce problems like the one you hit.因为您基本上减少了 Genson 的功能(现在人们只能使用字符串)并引入像您遇到的问题一样的问题。

I think your type parameter constraint were originally wrong;我认为您的类型参数约束最初是错误的; you had你有

trait DeserializeFromJson[T <: DeserializeFromJson[T]]

With your own answer, you fully relaxed it;有了你自己的答案,你就完全放松了; you needed你需要

trait DeserializeFromJson[T <: SerializeToJson]

...which the error was trying to tell you. ...错误试图告诉你。

The need for the implicit Manifest (ClassTag now I believe) or context-bounds was on the money.对隐式清单(现在我相信是 ClassTag)或上下文边界的需求是在金钱上。

Would be nice for Scala to allow the specification of inheritance and type-parameter constraints based on class/trait and companion object relationship, given it is already aware, to some degree, when it comes to access-modifiers and implicit scopes. Scala 允许基于类/特征和伴随对象关系的继承和类型参数约束的规范会很好,因为它在某种程度上已经意识到访问修饰符和隐式范围。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM