在 Scala 中,引用 Abstract Super 中的子类 Class

In Scala, Refer to Subclass in Abstract Super Class

有没有办法让超级 class 中的 this 关键字引用那个 class 的子 class?具体来说,我正在尝试执行以下操作(Json 指的是 Play 的 Json 库):

abstract class A() {
  def toJson[T](implicit writes: Writes[T]): JsValue = Json.toJson(this)
}

case class B(myProperty: String) extends A
object B { implicit val bFormat = Json.format[B] }

这给出了错误 No Json serializer found for type A. Try to implement an implicit Writes or Format for this type.。所以它说它不能序列化A类型的对象,这是有道理的。然而,目标是让 Json.toJson(this) 中的 this 引用子 class(在本例中,它是 B)。

有什么办法可以做到这一点吗?如果没有,有没有其他方法可以在 superclass 中实现 Json.toJson(...) 方法,而不必在 subclass (以及所有其他 subclasses 中实现A)?

从父类引用当前子类的常用技巧是使用 F-bounded polymorphism:

// Here `T` refers to the type of the current subclass
abstract class A[T <: A[T]]() { 
  this: T =>
  def toJson(implicit writes: Writes[T]): JsValue = Json.toJson(this)
}

// We have to specify the current subclass in `extends A[B]`
case class B(myProperty: String) extends A[B]
object B { implicit val bFormat = Json.format[B] }

println(B("foo").toJson)

这不允许您为任何通用 A 调用 toJson

val a: A[_] = B("foo")
println(a.toJson)      // Doesn't compile with: 
                       //   No Json serializer found for type _. 
                       //   Try to implement an implicit Writes or Format for this type.

要解决此问题,您必须在创建对象时为子类型保存 Writes

abstract class A[T <: A[T]](implicit writes: Writes[T]) { 
  this: T =>
  def toJson: JsValue = Json.toJson(this)
}

或者使用 context bound 表示法:

abstract class A[T <: A[T] : Writes] { 
  this: T =>
  def toJson: JsValue = Json.toJson(this)
}

而且由于这个 F 有界多态性只是一个实现细节,并且总是引用通用 A 作为 A[_] 是相当样板的,您可以将此代码移动到中间 abstract class.

所以一个完整的例子是这样的:

abstract class A() {
  def toJson: JsValue
}

abstract class AImpl[T <: AImpl[T] : Writes] extends A { 
  this: T =>
  def toJson: JsValue = Json.toJson(this)
}

case class B(myProperty: String) extends AImpl[B]
object B { implicit val bFormat: Format[B] = Json.format[B] }

val a: A = B("foo")
println(a.toJson)