2017-07-23 167 views
1

我試圖制定一個將任意值轉換爲Json表示並且在未定義轉換的情況下編譯時錯誤的經典示例。隱式轉換不適用

到目前爲止,我有,

trait Json 

    trait ConvertableToJson[A] { 
    def toJson: Json 
    } 

    object Json { 

    case class Str(str: String) extends Json 

    case class Int(int : Int) extends Json 

    case class StrToJson(s: String) extends ConvertableToJson[StrToJson] { 
     override def toJson: Json = Str(s) 
    } 

    } 

    implicit def str2Json(s: String): StrToJson = StrToJson(s) 

    def toJson[A <: ConvertableToJson[A]](a: A) = a.toJson 

    println(toJson("some string")) 

我希望上面的代碼一樣的工作:

toJson("some string")失敗而不implicit def編譯。因爲String <: ConvertableToJson[String]是錯誤的。

但後來用,implicit def並找到Str2Json

Str2Json <: ConvertableToJson[Str2Json]應該是正確的。

不過,這並沒有發生,編譯器抱怨:

Error: Inferred type arguments [String] do not conform to method toJson's type parameter bounds [A <: scalaz.ConvertToJson.ConvertableToJson[A]] 
    println(toJson("dhruv")) 
     ^

這將是巨大的,如果有人能幫助我糾正我的理解

回答

2

因此,有兩個問題與您的代碼。首先String不會延伸ConvertableToJson[String],這是你最後一次函數調用正在做的事情。

第二個case class StrToJson應延伸ConvertableToJson[String]而不是ConvertableToJson[StrToJson]

然後通過使用視圖邊界<%(參見下面的工作示例)編譯代碼。然而,這是一個壞主意,因爲視圖範圍已被棄用爲語言功能,您應該改用類型類。

trait Json 

trait ConvertableToJson[A] { 
    def toJson: Json 
} 

object Json { 

    case class Str(str: String) extends Json 

    case class Int(int : Int) extends Json 

    case class StrToJson(s: String) extends ConvertableToJson[String] { 
    override def toJson: Json = Str(s) 
    } 

} 

import Json._ 

implicit def str2Json(s: String): StrToJson = StrToJson(s) 

def toJson[A <% ConvertableToJson[A]](a: A) = a.toJson 

println(toJson("some string")) 

使用類型類

trait Json 

trait ConvertableToJson[A] { 
    // NOTE: this now takes a parameter 
    def toJson(a: A): Json 
} 

object Json { 

    case class Str(str: String) extends Json 

    case class Int(int : Int) extends Json 
} 

import Json._ 

// NOTE: Because toJson takes a parameter the implicit implementation can now be an object 
implicit object Str2Json extends ConvertableToJson[String] { 
    override def toJson(a: String): Json = Str(a) 
} 

// NOTE: If you want to support the a.toJson syntax this implicit class adds it for all types with an implicit ConvertableToJson 
implicit class ConvertableToJsonSyntax[A](a: A)(implicit ev: ConvertableToJson[A]) { 
    def toJson: Json = ev.toJson(a) 
} 

// NOTE: Now we use context-bounds instead of view-bounds 
def toJson[A : ConvertableToJson](a: A) = a.toJson 

// NOTE: we can expand the context-bounds 
def toJson2[A](a: A)(implicit ev: ConvertableToJson[A]) = a.toJson 

// NOTE: But since we have the type class instance now, we do not need the extra syntax 
def toJson3[A](a: A)(implicit ev: ConvertableToJson[A]) = ev.toJson(a) 

println(toJson("some string")) 
+0

我明白String不延伸ConvertableToJson [字符串]這就是爲什麼我所定義的隱含DEF str2Json與簽名,字符串 - > ConvertableToJson(StrToString)。在我的理解中,通過這個隱式def,scala編譯器應該能夠將String轉換爲ConvertableToJson [StrToString],它應該與toJson很好地配合。當然,它不起作用,你的實現,但我仍然不明白爲什麼我的工作。謝謝! :) –

+0

再看看它可以工作,如果你把'toJson'的定義改爲'def toJson [A](a:ConvertableToJson [A])= a.toJson'。隱式轉換僅在將值傳遞到採用不同類型的變量/參數時觸發。它們不會觸發通用參數類型。 (如果您將通用參數從「ConvertableToJson」中刪除,您的代碼將繼續工作,因爲它從不使用。 – iain