2016-04-15 61 views
4

我想將類型傳遞給Scala中的函數。在Scala中傳遞類型作爲參數

詳細

第一問題迭代

我有以下Java類(從外部源):

public class MyComplexType { 
    public String name; 
    public int number; 
} 

public class MyGeneric<T> { 
    public String myName; 
    public T myValue; 
} 

在這個例子中,我想MyComplexTypeMyGeneric的實際類型;在真正的問題中有幾種可能性。

我想用Scala代碼如下反序列化JSON字符串:

import org.codehaus.jackson.map.ObjectMapper 

object GenericExample { 
    def main(args: Array[String]) { 
    val jsonString = "{\"myName\":\"myNumber\",\"myValue\":{\"name\":\"fifteen\",\"number\":\"15\"}}" 
    val objectMapper = new ObjectMapper() 
    val myGeneric: MyGeneric[MyComplexType] = objectMapper.readValue(jsonString, classOf[MyGeneric[MyComplexType]]) 
    val myComplexType: MyComplexType = myGeneric.myValue 
    } 
} 

它編譯罰款,但會出現運行時錯誤:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to MyComplexType 
     at GenericExample$.main(GenericExample.scala:9) 

第二次迭代

工作液到問題:

val jsonString = "{\"myName\":\"myNumber\",\"myValue\":{\"name\":\"fifteen\",\"number\":\"15\"}}" 
val objectMapper = new ObjectMapper() 
val myGeneric: MyGeneric[MyComplexType] = objectMapper.readValue(jsonString, classOf[MyGeneric[MyComplexType]]) 
myGeneric.myValue = objectMapper.readValue(objectMapper.readTree(jsonString).get("myValue").toString, classOf[MyComplexType]) 
val myComplexType: MyComplexType = myGeneric.myValue 

不好但工作。 (如果有人知道如何使它更好,也歡迎。)

第三次迭代

在第二次迭代的解決方案中的行發生在真正的問題好幾次,所以我想創建一個功能。更改變量是JSON格式的字符串和MyComplexType

我想是這樣的:

def main(args: Array[String]) { 
    val jsonString = "{\"myName\":\"myNumber\",\"myValue\":{\"name\":\"fifteen\",\"number\":\"15\"}}" 
    val myGeneric = extractMyGeneric[MyComplexType](jsonString) 
    val myComplexType: MyComplexType = myGeneric.myValue 
} 

private def extractMyGeneric[T](jsonString: String) = { 
    val objectMapper = new ObjectMapper() 
    val myGeneric = objectMapper.readValue(jsonString, classOf[MyGeneric[T]])  
    myGeneric.myValue = objectMapper.readValue(objectMapper.readTree(jsonString).get("myValue").toString, classOf[T]) 
    myGeneric 
} 

這不起作用(編譯錯誤)。我已經玩過Class,ClassTag,classOf的各種組合,但都沒有幫助。還有編譯器和運行時錯誤。你知道如何通過以及如何在Scala中使用這樣的類型嗎?謝謝!

回答

1

當您使用jackson解析json時,可以使用TypeReference解析generic類型。例如:

val jsonString = "{\"myName\":\"myNumber\",\"myValue\":{\"name\":\"fifteen\",\"number\":\"15\"}}" 
val objectMapper = new ObjectMapper() 
val reference = new TypeReference[MyGeneric[MyComplexType]]() {} 
val value: MyGeneric[MyComplexType] = objectMapper.readValue(jsonString, reference) 

,如果你仍然想使用Jackson,我想你可以創建一個參數與TypeReference型。像:

implicit val typeReference = new TypeReference[MyGeneric[MyComplexType]] {} 
    val value = foo(jsonString) 
    println(value.myValue.name) 


    def foo[T](jsonStr: String)(implicit typeReference: TypeReference[MyGeneric[T]]): MyGeneric[T] = { 
    val objectMapper = new ObjectMapper() 
    objectMapper.readValue(jsonStr, typeReference) 
    } 
+0

謝謝你的回答!爲了擴展它:'TypeReference'應該被導入,如下所示:'import org.codehaus.jackson。\'type \'.TypeReference'。 –

+0

這是「第一次迭代」問題的不錯解決方案,謝謝!但是,它並沒有解決「第三次迭代」中提出的問題,即主要問題。這仍然是開放的;事實上還有其他一些事情要做,因此創建一個單獨的函數會很好。最直接的解決方案(將MyGeneric [MyComplexType]作爲泛型傳遞)導致運行時錯誤java.lang.ClassCastException:java.util.LinkedHashMap不能轉換爲MyGeneric。 –

+0

@CsabaFaragó你可以檢查我的更新,希望它可以幫助你。 – chengpohi

1

用你的方法,我覺得這是你可以得到你需要使用ClassTag小號類:

def extractMyGeneric[A : ClassTag](jsonString: String)(implicit generic: ClassTag[MyGeneric[A]]): MyGeneric[A] = { 
    val classOfA = implicitly[ClassTag[A]].runtimeClass.asInstanceOf[Class[A]] 
    val classOfMyGenericOfA = generic.runtimeClass.asInstanceOf[Class[MyGeneric[A]]] 
    val objectMapper = new ObjectMapper() 
    val myGeneric = objectMapper.readValue(jsonString, classOfMyGenericOfA) 
    myGeneric.myValue = objectMapper.readValue(objectMapper.readTree(jsonString).get("myValue").toString, classOfA) 
    myGeneric 
} 

我不熟悉傑克遜,但在播放JSON,你可以很容易地定義Reads您的泛型類這樣

import play.api.libs.functional.syntax._ 
import play.api.libs.json._ 

implicit def genReads[A: Reads]: Reads[MyGeneric[A]] = (
    (__ \ "myName").read[String] and 
    (__ \ "myValue").read[A] 
)((name, value) => { 
    val e = new MyGeneric[A] 
    e.myName = name 
    e.myValue = value 
    e 
}) 

到這一點,並提供了Reads該實例爲MyComplexType存在,Y OU可以實現你的方法,

def extractMyGeneric[A: Reads](jsonString: String): MyGeneric[A] = { 
    Json.parse(jsonString).as[MyGeneric[A]] 
} 

這裏的問題是,您需要提供Reads您的所有複雜類型的,如果這些人case類這將是作爲

implicit complexReads: Reads[MyComplexType] = Json.reads[MyComplexType] 

容易,otherways我認爲你必須以與我已用genReads完成的工作相似的方式手動定義它們。

+0

謝謝您的詳細解答!與此同時,另一個線程也解決了我的問題,同時傑克遜也有改進。我保存您的解決方案,以查找可能存在的更多類型相關問題 –