Json 将泛型伴随对象传递给超级构造函数
我正试图构造一个Json 将泛型伴随对象传递给超级构造函数,json,scala,generics,companion-object,Json,Scala,Generics,Companion Object,我正试图构造一个trait和一个抽象类,以便通过消息(在Akka游戏环境中)将它们转换为子类型,这样我就可以轻松地将它们转换为Json 迄今为止所做的工作: abstract class OutputMessage(val companion: OutputMessageCompanion[OutputMessage]) { def toJson: JsValue = Json.toJson(this)(companion.fmt) } trait
trait
和一个抽象类
,以便通过消息(在Akka游戏环境中)将它们转换为子类型,这样我就可以轻松地将它们转换为Json
迄今为止所做的工作:
abstract class OutputMessage(val companion: OutputMessageCompanion[OutputMessage]) {
def toJson: JsValue = Json.toJson(this)(companion.fmt)
}
trait OutputMessageCompanion[OT] {
implicit val fmt: OFormat[OT]
}
问题是,当我试图实现以下提到的接口时:
case class NotifyTableChange(tableStatus: BizTable) extends OutputMessage(NotifyTableChange)
object NotifyTableChange extends OutputMessageCompanion[NotifyTableChange] {
override implicit val fmt: OFormat[NotifyTableChange] = Json.format[NotifyTableChange]
}
我从Intellij获得此错误:
类型不匹配,应为:OutputMessageCompanion[OutputMessage],实际为:NotifyTableChange.Type
我对Scala泛型有点陌生,所以如果能帮我解释一下,我将不胜感激
另外,我愿意接受任何比上面提到的更通用的解决方案。
目标是,当获取
OutputMessage
的任何子类型时,为了方便地将其转换为Json
,编译器说您的同伴
在OutputMessage
上定义为通用参数,而不是某些特定的子类型。要解决这个问题,您需要使用一个称为的技巧。另外,我不喜欢在每条消息中以val
的形式存储伴随对象的想法(毕竟你不希望它序列化,是吗?)。将其定义为def
是一种更好的权衡。代码如下所示(保持不变):
显然,您也希望从JSON解码,但仍然需要一个伴生对象
对评论的回答
val
声明的所有内容都会在对象(类的实例)中存储一个字段。默认情况下,序列化程序尝试序列化所有字段。通常有某种方式可以说某些字段应该被忽略(比如一些@IgnoreAnnotation
)。另外,这意味着每个对象中都会有一个以上的指针/引用,这些指针/引用会毫无理由地使用内存,这对您来说可能是个问题,也可能不是。将其声明为def
将获得一个方法,这样您就可以将一个对象存储在某个“静态”位置,如同伴对象,或者每次都按需构建它
对象
概念的所有用例。作为第一条经验法则,对象有两种主要用法:
您可以在其他语言中使用static
,即静态方法、常量和静态变量的容器(尽管不鼓励使用变量,尤其是Scala中的静态变量)
单例模式的实现
f-bound generic-您是指M的下限被输出消息[M](顺便问一下,为什么在同一个表达式中使用M两次是可以的?)
不幸的是,wiki只提供了一个基本的描述。F-有界多态性的整体思想是能够以某种通用方式访问基类类型中的子类类型。通常A编译器会说您的同伴
是在OutputMessage
上定义为泛型参数,而不是某些特定的子类型。要解决这个问题,您需要使用一个称为的技巧。另外,我不喜欢在每条消息中以val
的形式存储伴随对象的想法(毕竟你不希望它序列化,是吗?)。将其定义为def
是一种更好的权衡。代码如下所示(保持不变):
显然,您也希望从JSON解码,但仍然需要一个伴生对象
对评论的回答
通过def-引用同伴意味着这是一个“方法”,因此对子类型的所有实例定义一次(并且不会序列化)
使用val
声明的所有内容都会在对象(类的实例)中存储一个字段。默认情况下,序列化程序尝试序列化所有字段。通常有某种方式可以说某些字段应该被忽略(比如一些@IgnoreAnnotation
)。另外,这意味着每个对象中都会有一个以上的指针/引用,这些指针/引用会毫无理由地使用内存,这对您来说可能是个问题,也可能不是。将其声明为def
将获得一个方法,这样您就可以将一个对象存储在某个“静态”位置,如同伴对象,或者每次都按需构建它
我对Scala有点陌生,我已经养成了将格式放在伴生对象中的习惯,你会推荐/参考一些源代码,关于如何决定将方法最好放在哪里
Scala是一种不寻常的语言,没有直接映射,它涵盖了其他语言中对象
概念的所有用例。作为第一条经验法则,对象有两种主要用法:
您可以在其他语言中使用static
,即静态方法、常量和静态变量的容器(尽管不鼓励使用变量,尤其是Scala中的静态变量)
单例模式的实现
f-bound generic-您是指M的下限被输出消息[M](顺便问一下,为什么在同一个表达式中使用M两次是可以的?)
不幸的是,wiki只提供了一个基本的描述。F-有界多态性的整体思想是能够以某种通用方式访问基类类型中的子类类型。通常,A正如SergGr已经回答的那样,您需要一种F-有界的多态性来解决这个问题,因为现在是这样。
然而,对于这些情况,我认为(请注意,这只是我的意见)最好使用
abstract class OutputMessage[M <: OutputMessage[M]]() {
self: M => // required to match Json.toJson signature
protected def companion: OutputMessageCompanion[M]
def toJson: JsValue = Json.toJson(this)(companion.fmt)
}
case class NotifyTableChange(tableStatus: BizTable) extends OutputMessage[NotifyTableChange] {
override protected def companion: OutputMessageCompanion[NotifyTableChange] = NotifyTableChange
}
abstract class OutputMessage[M <: OutputMessage[M]]() {
self: M => // required to match Json.toJson signature
implicit protected def fmt: OFormat[M]
def toJson: JsValue = Json.toJson(this)
}
case class NotifyTableChange(tableStatus: BizTable) extends OutputMessage[NotifyTableChange] {
override implicit protected def fmt: OFormat[NotifyTableChange] = Json.format[NotifyTableChange]
}
class Child extends OutputMessage[Child}
abstract class OutputMessage[M]() {
self: M =>
...
}
object syntax {
object json {
implicit class JsonOps[T](val t: T) extends AnyVal {
def toJson(implicit: fmt: OFormat[T]): JsVal = Json.toJson(t)(fmt)
}
}
}
final case class NotifyTableChange(tableStatus: BizTable)
object NotifyTableChange {
implicit val fmt: OFormat[NotifyTableChange] = Json.format[NotifyTableChange]
}
import syntax.json._
val m = NotifyTableChange(tableStatus = ???)
val mJson = m.toJson // This works!