2017-08-14 2 views
0

Je suis en train de convertir une carte Scala [String, Tous] à une classe de cas utilisant la réflexion Scala (Scala 2,11) comme suit: -Carte Scala à la conversion de cas de classe

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

cela se traduit malheureusement dans une compilation erreur -

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

Je suis assez nouveau à l'utilisation ClassTags et je comprends que cette erreur est principalement parce que java.lang.Object est un sous-ensemble de tout et de Tout pourrait inclure des objets non-java.

Lorsque j'ai essayé de remplacer Any par AnyRef (qui correspond à java.lang.Object dans JRE), l'appel de fonction entraîne une erreur de type incompatibilité.

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) 

Quelle est la meilleure façon de résoudre cette erreur? Suggestions appréciées. Merci!

Mise à jour 1 - La mise à jour de cette commande pour implicitement passer un Any à AnyRef conduit à une erreur 'java.util.NoSuchElementException' lors d'un appel de fonction.

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) 

Qu'est-ce que je fais mal ici?

+0

Si vous le faites comme Java, vous devez passer dans l'instance de la classe externe dans le paramètre 'outer' $. – HTNW

Répondre

2

Utilisez un casting:

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

Notez que vous associez tous les domaines d'une classe de cas à un constructeur param. C'est incorrect, car une classe de cas peut avoir des champs qui ne sont pas dans le constructeur, et votre code va se casser.

Une meilleure idée est d'utiliser la réflexion 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

Merci HTNW. Mais il en résulte une erreur - Nom: scala.ScalaReflectionException Message: class someCC est une classe interne, utilisez reflectClass sur un InstanceMirror pour obtenir son 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) à scala.reflect.runtime.JavaMirrors $ JavaMirror.reflectClass (JavaMirrors. scala: 183) at scala.reflect.runtime.JavaMirrors $ JavaMirror.reflectClass (JavaMirrors.scala – Yash

+1

Oui, cela nécessite un peu de peaufinage pour les classes internes, donnez-moi un moment.) – HTNW

+0

Merci. Pourriez-vous élaborer sur Comment utiliser? extérieur dans ce scénario spécifique? – Yash