I am trying to instantiate case classes using reflection. I am using this reference code: https://gist.github.com/ConnorDoyle/7002426
import scala.reflect.runtime.universe._
object ReflectionHelpers extends ReflectionHelpers
trait ReflectionHelpers {
protected val classLoaderMirror = runtimeMirror(getClass.getClassLoader)
/**
* Encapsulates functionality to reflectively invoke the constructor
* for a given case class type `T`.
*
* @tparam T the type of the case class this factory builds
*/
class CaseClassFactory[T: TypeTag] {
val tpe = typeOf[T]
val classSymbol = tpe.typeSymbol.asClass
if (!(tpe <:< typeOf[Product] && classSymbol.isCaseClass))
throw new IllegalArgumentException(
"CaseClassFactory only applies to case classes!"
)
val classMirror = classLoaderMirror reflectClass classSymbol
val constructorSymbol = tpe.declaration(nme.CONSTRUCTOR)
val defaultConstructor =
if (constructorSymbol.isMethod) constructorSymbol.asMethod
else {
val ctors = constructorSymbol.asTerm.alternatives
ctors.map { _.asMethod }.find { _.isPrimaryConstructor }.get
}
val constructorMethod = classMirror reflectConstructor defaultConstructor
/**
* Attempts to create a new instance of the specified type by calling the
* constructor method with the supplied arguments.
*
* @param args the arguments to supply to the constructor method
*/
def buildWith(args: Seq[_]): T = constructorMethod(args: _*).asInstanceOf[T]
}
}
Everything works great for most types of fields in the case class. My only issue is when trying to populate array fields.
Basically it boils down to this issue:
case class MyClass(arr: Array[String])
object Main {
def main(args: Array[String]): Unit = {
val f = new CaseClassFactory[MyClass]
f.buildWith(Seq(Array[String]("1","2","3"))) //This works
f.buildWith(Seq(Array[Object]("1","2","3"))) //This does not work
}
}
Exception in thread "main" java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at scala.reflect.runtime.JavaMirrors$JavaMirror$JavaVanillaMethodMirror1.jinvokeraw(JavaMirrors.scala:415)
at scala.reflect.runtime.JavaMirrors$JavaMirror$JavaMethodMirror.jinvoke(JavaMirrors.scala:380)
at scala.reflect.runtime.JavaMirrors$JavaMirror$JavaVanillaMethodMirror.apply(JavaMirrors.scala:396)
In my use case the array is being created using reflection as well as an Array[Object]. I have the scala.reflect.api.Types#Type of the array field of the case class. I need to somehow convert the existing array to one with the correct type I understand the internal implementation of arrays causes the issue. How can I convert the array to the needed type at runtime. I tried to use Arrays.copyOf but did not get it to work
Aucun commentaire:
Enregistrer un commentaire