2017-08-14 175 views
0

我想Scala的地圖[字符串,任何]轉換爲使用Scala的反射(斯卡拉2.11)的情況下,類如下 -斯卡拉地圖案例類轉換

val m = Map("name" -> "ABC", "age" -> 7, "gender" -> "male") 
case class someCC(name: String, age: Int, gender: String) 

import scala.reflect.ClassTag 

    def createCaseClass[T](someMap : Map[String, Any])(implicit someClassTag : ClassTag[T]) = { 

    val ctor = someClassTag.runtimeClass.getConstructors.head 
    val args = someClassTag.runtimeClass.getDeclaredFields.map(x => someMap(x.getName)) 

     ctor.newInstance(args: _*).asInstanceOf[T] 
    } 

這個不幸導致編譯錯誤 -

Name: Unknown Error 
Message: <console>:106: error: type mismatch; 
found : Array[Any] 
required: Array[_ <: Object] 
Note: Any >: Object, but class Array is invariant in type T. 
You may wish to investigate a wildcard type such as `_ >: Object`. (SLS 3.2.10) 
       ctor.newInstance(args: _*).asInstanceOf[T] 
         ^

我是相當新的使用ClassTags,我明白,這個錯誤主要是因爲java.lang.Object繼承是任何一個子集,任何可能包括非Java對象。

當我嘗試將Any替換爲AnyRef(它對應於JRE中的java.lang.Object)時,該函數調用會導致類型不匹配錯誤。

import scala.reflect.ClassTag 

    def createCaseClass[T](someMap : Map[String, AnyRef])(implicit someClassTag : ClassTag[T]) = { 

    val ctor = someClassTag.runtimeClass.getConstructors.head 
    val args = someClassTag.runtimeClass.getDeclaredFields.map(x => someMap(x.getName)) 

     ctor.newInstance(args: _*).asInstanceOf[T] 
    } 

val someCC = createCaseClass[someCC](m) 

Name: Unknown Error 
Message: <console>:106: error: type mismatch; 
found : scala.collection.immutable.Map[String,Any] 
required: Map[String,AnyRef] 
     val someCC = createCaseClass[someCC](m) 

解決此錯誤的最佳方法是什麼?建議感激。謝謝!

更新1 - 更新此隱式地將Any轉換爲AnyRef導致函數調用時出現錯誤「java.util.NoSuchElementException」。

import scala.reflect.ClassTag 
import scala.reflect.runtime.universe._ 

    def createMyClass[T](someMap : Map[String, Any])(implicit someClassTag : ClassTag[T]) = { 

     val ctor = someClassTag.runtimeClass.getConstructors.head 

     val args = someClassTag.runtimeClass.getDeclaredFields.map(x => someMap(x.getName)) 

     ctor.newInstance(args.asInstanceOf[Seq[AnyRef]]: _*).asInstanceOf[T] 

} 

val m = Map("name" -> "ABC", "age" -> 7, "gender" -> "male") 
case class someCC(name: String, age: Int, gender: String) 

createMyClass[someCC](m) 

Name: java.util.NoSuchElementException 
Message: key not found: $outer 
StackTrace: at scala.collection.MapLike$class.default(MapLike.scala:228) 
    at scala.collection.AbstractMap.default(Map.scala:59) 
    at scala.collection.MapLike$class.apply(MapLike.scala:141) 
    at scala.collection.AbstractMap.apply(Map.scala:59) 
    at $$$e75186ae1b35495ffea8e318378149a$$$$anonfun$1.apply(<console>:135) 
    at $$$e75186ae1b35495ffea8e318378149a$$$$anonfun$1.apply(<console>:135) 
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234) 
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234) 
    at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33) 
    at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186) 
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:234) 
    at scala.collection.mutable.ArrayOps$ofRef.map(ArrayOps.scala:186) 
    at createMyClass(<console>:135) 

我在這裏做錯了什麼?

+0

如果你這樣做Java的方式,你需要在參數'$ outer'外部類的實例來傳遞。 – HTNW

回答

2

使用強制:

ctor.newInstance(args.asInstanceOf[Seq[AnyRef]]: _*).asInstanceOf[T] 

請注意,您映射的情況下類的每個場構造PARAM。這是不正確的,因爲case類可能有不在構造函數中的字段,並且您的代碼會中斷。

更好的方法是使用Scala的反思:

import reflect.runtime.universe._ 

def mkClassInstance[T: TypeTag](args: Map[String, Any]): T = { 
    val rMirror = runtimeMirror(getClass.getClassLoader) 
    val cMirror = rMirror.reflectClass(typeOf[T].typeSymbol.asClass) 
    // The primary constructor is the first one 
    val ctor = typeOf[T].decl(termNames.CONSTRUCTOR).asTerm.alternatives.head.asMethod 
    val argList = ctor.paramLists.flatten.map(param => args(param.name.toString)) 
    cMirror.reflectConstructor(ctor)(argList: _*).asInstanceOf[T] 
} 
def mkInnerClassInstance[T: TypeTag](outer: Any)(args: Map[String, Any]): T = { 
    val rMirror = runtimeMirror(getClass.getClassLoader) 
    val cMirror = rMirror.reflect(outer).reflectClass(typeOf[T].typeSymbol.asClass) 
    // The primary constructor is the first one 
    val ctor = typeOf[T].decl(termNames.CONSTRUCTOR).asTerm.alternatives.head.asMethod 
    val argList = ctor.paramLists.flatten.map(param => args(param.name.toString)) 
    cMirror.reflectConstructor(ctor)(argList: _*).asInstanceOf[T] 
} 
+0

謝謝HTNW。但是,這會導致錯誤 - 名稱:scala.ScalaReflectionException 消息:類someCC是一個內部類,在InstanceMirror上使用reflectClass來獲取其ClassMirror StackTrace:at scala.reflect.runtime.JavaMirrors $ JavaMirror.scala $ reflect $ runtime $ JavaMirrors $ JavaMirror $$ abort(JavaMirrors.scala:115) at scala.reflect.runtime.JavaMirrors $ JavaMirror.ErrorInnerClass(JavaMirrors.scala:117) at scala.reflect.runtime.JavaMirrors $ JavaMirror.reflectClass(JavaMirrors。 scala:183) at scala.reflect.runtime.JavaMirrors $ JavaMirror.reflectClass(JavaMirrors.scala – Yash

+1

是的,這需要一些內部類的調整;給我一點時間 – HTNW

+0

謝謝你能詳細說明我將如何使用外部在這個特定的場景? – Yash