簡體   English   中英

Scala映射到案例類轉換

[英]Scala Map to Case Class conversion

我正在嘗試使用Scala反射(Scala 2.11)將Scala Map [String,Any]轉換為案例類,如下所示-

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]
                           ^

我是使用ClassTag的新手,我知道此錯誤主要是因為java.lang.Object是Any的子集,並且Any可能包含非Java對象。

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

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)

我在這里做錯了什么?

使用演員表:

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

請注意,您將案例類的每個字段都映射到構造函數參數。 這是不正確的,因為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]
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM