【问题标题】:Scala Map to Case Class conversionScala 映射到案例类的转换
【发布时间】:2017-08-14 19:45:40
【问题描述】:

我正在尝试使用 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]
                           ^

我对使用 ClassTags 相当陌生,我知道这个错误主要是因为 java.lang.Object 是 Any 的子集,并且 Any 可能包含非 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 - 更新 this 以将 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)

我在这里做错了什么?

【问题讨论】:

  • 如果用Java方式做,需要在参数$outer中传入外层类实例。

标签: scala reflection scala-collections classtag


【解决方案1】:

使用演员表:

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

请注意,您将案例类的 每个 字段映射到构造函数参数。这是不正确的,因为案例类可以包含不在构造函数中的字段,并且您的代码会中断。

一个更好的主意是使用 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]
}

【讨论】:

  • 感谢 HTNW。但这会导致错误 - 名称:scala.ScalaReflectionException 消息:类 someCC 是一个内部类,在 InstanceMirror 上使用 reflectClass 来获取其 ClassMirror StackTrace:在 scala.reflect.runtime.JavaMirrors$JavaMirror.scala$reflect$runtime$JavaMirrors $JavaMirror$$abort(JavaMirrors.scala:115) 在 scala.reflect.runtime.JavaMirrors$JavaMirror.ErrorInnerClass(JavaMirrors.scala:117) 在 scala.reflect.runtime.JavaMirrors$JavaMirror.reflectClass(JavaMirrors.scala:183)在 scala.reflect.runtime.JavaMirrors$JavaMirror.reflectClass(JavaMirrors.scala
  • 是的,这需要对内部类进行一些调整;给我一点时间。
  • 谢谢。您能否详细说明在这种特定情况下我将如何使用外部?
  • mkInnerClassInstance[outer.SomeClass](outer)(Map(...))
  • val a = someCC("ABC", 0, "male") mkInnerClassInstance[someCC](a)(m) 这是不正确的吗?您能否提供一个函数调用的示例,因为我很难理解它?
猜你喜欢
  • 1970-01-01
  • 2021-10-12
  • 2010-11-16
  • 1970-01-01
  • 1970-01-01
  • 2013-01-10
  • 2014-09-03
  • 2016-07-27
相关资源
最近更新 更多