2016-02-26 103 views
0

超類中的this關鍵字是否可以引用該類的子類?具體來說,我想做到以下幾點(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)指的是子類(在這種情況下,它是B)。

有什麼辦法可以做到這一點?如果沒有,有沒有其他方法可以在超類中實現Json.toJson(...)方法,而無需在子類(以及A的所有其他子類)中實現?

+0

您可以使用F-界多態性http://stackoverflow.com/questions/14741047/polymorphic-scala-return-type –

+0

我會建議使用改爲類型類。例如。 http://argonaut.io/doc/codec/ – Reactormonk

回答

3

的常見的伎倆是指從父到子類電流,是使用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) 

這會不會讓你打電話,雖然對toJson任何通用A

val a: A[_] = B("foo") 
println(a.toJson)  // Doesn't compile with: 
         // No Json serializer found for type _$1. 
         // 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[_]相當樣板-Y,你可以將這些代碼到中間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) 
+0

爲什麼'A'和'AImpl'抽象類而不是特徵? –

+2

@PimVerkerk OP有一個抽象類。另外,雖然'A'很容易成爲一個特徵,但將'AImpl'作爲一個抽象類來使用它可能會帶來一個隱含的爭論。使'AImpl'成爲特性需要在每個子類中手動創建這個隱式的'Writes'參數。 – Kolmar

+0

謝謝。有趣的是,如果不明確設置'bFormat'屬性的類型,就不會編譯。和in一樣,'implicit val bFormat = Json.format [B]'不會被編譯。你知道爲什麼是這樣嗎?爲什麼不能從'Json.format [B]'表達式中推斷出它的類型? – socom1880