If you know parent names at compile time you can write a macro (see sbt settings for macro projects)
import scala.language.experimental.macros
import scala.reflect.macros.{blackbox, whitebox}
def makeInstance[A](parentNames: String*): A = macro makeInstanceImpl[A]
def makeInstanceImpl[A: c.WeakTypeTag](
c: whitebox/*blackbox*/.Context
)(parentNames: c.Tree*): c.Tree = {
import c.universe._
def getValue(tree: Tree): String = tree match {
case q"${name: String}" => name
}
// def getValue(tree: Tree): String = c.eval(c.Expr[String](c.untypecheck(tree)))
val parents = parentNames.map(name => tq"${TypeName(getValue(name))}")
q"new ${weakTypeOf[A]} with ..$parents"
}
Please notice that if you make a macro whitebox then it can return a value of more precise type than declared (A
)
val instance = makeInstance[ComposedTrait]("SubA2", "SubA1")
// checking the type
instance: ComposedTrait with SubA2 with SubA1 // not just ComposedTrait
// scalacOptions += "-Ymacro-debug-lite"
//scalac: {
// final class $anon extends ComposedTrait with SubA2 with SubA1 {
// def <init>() = {
// super.<init>();
// ()
// }
// };
// new $anon()
//}
Scala Reflection--Create Instance from class name string of this class type
If you know parent names at runtime you can use reflective toolbox (runtime compilation)
import scala.reflect.runtime
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
val rm = runtime.currentMirror
val tb = rm.mkToolBox()
def makeInstance[A: TypeTag](parentNames: String*): A = {
val parents = parentNames.map(name => tq"${TypeName(name)}")
tb.eval(q"new ${typeOf[A]} with ..$parents").asInstanceOf[A]
}
makeInstance[ComposedTrait]("SubA2", "SubA1")
Please notice that in such case statically you can't have a value of more precise type than ComposedTrait
based on runtime strings (parent names).
If you need to create ComposedTrait
at runtime you can do this as well
val classSymbol = tb.define(q"trait ComposedTrait".asInstanceOf[ClassDef])
def makeInstance(parentNames: String*): Any = {
val parents = parentNames.map(name => tq"${TypeName(name)}")
tb.eval(q"new $classSymbol with ..$parents")
}
makeInstance("SubA2", "SubA1")