I compiled this working code that dynamically creates an instance of the class from the given name: String (see below). This works for simple case classes and case classes that have one constructor.
I'm not sure how to handle the general case when there are multiple constructors. I suppose that they can be distinguished only by their signatures, but finding a conceptual interaction between related objects, classes and classes of events is a bit confusing. What will be the full description of all the scenarios of "construction" that need to be taken into account, or, how would you develop this to become completely general?
Demonstrating its inflexibility, this code awkwardly fails when viewing a companion object.
Hopefully this code will give a convenient test and stream to fiddle if someone needs to join this thought.
import scala.reflect.runtime.universe case class CaseClass(foo: Int) { println(s"${getClass.getSimpleName} Instantiated with $foo") } class BaseClass(foo: Int) { println(s"${getClass.getSimpleName} Instantiated with $foo") } object BaseClass { def apply(foo:Int) = { println(s"going through companion object ${getClass.getSimpleName}") new BaseClass(foo+10) } } class GenericClass[T](foo: T) { println(s"${getClass.getSimpleName} Instantiated with $foo") } object Inst { private def log(s: String) = println(Console.YELLOW + Console.BOLD + s + Console.RESET) private def selectConstructor(symbol: universe.Symbol) = { val constructors = symbol.typeSignature.members.filter(_.isConstructor).toList if (constructors.length > 1) log( s"""Warning: $symbol has several constructors, arbitrarily picking the first one: | ${constructors.mkString("\n ")}""".stripMargin) constructors.head.asMethod } def apply(className: String, arg: Any) = { val runtimeMirror: universe.Mirror = universe.runtimeMirror(getClass.getClassLoader) val classSymbol: universe.ClassSymbol = runtimeMirror.classSymbol(Class.forName(className)) val classMirror: universe.ClassMirror = runtimeMirror.reflectClass(classSymbol) if (classSymbol.companion.toString() == "<none>") // TODO: use nicer method "hiding" in the api? { log(s"Info: $className has no companion object") val constructorMirror = classMirror.reflectConstructor(selectConstructor(classSymbol)) // we can reuse it constructorMirror(arg) } else { val companionSymbol = classSymbol.companion log(s"Info: $className has companion object $companionSymbol") val constructorMirror = classMirror.reflectConstructor(selectConstructor(classSymbol)) // we can reuse it constructorMirror(arg) } } } object Test extends App { val c1 = Inst("BaseClass", 3) val c2 = Inst("BaseClass", 4) val cc = Inst("CaseClass", 5) val gc1 = Inst("GenericClass", "I am generic") val gc2 = Inst("GenericClass", gc1) println(s"""\nthese objects have been instantiated:\n${List(c1,c2,cc,gc1,gc2).mkString("\n")}""") }
Note that this requires build.sbt or similar:
lazy val reflection = (project in file(".")) .settings( scalaVersion := "2.11.7", libraryDependencies ++= Seq( "org.scala-lang" % "scala-compiler" % scalaVersion.value % "provided", "org.scala-lang" % "scala-library" % scalaVersion.value % "provided" ) )
scala
matanster Dec 11 '15 at 16:18 2015-12-11 16:18
source share