我已经整理了这个工作代码,可以从给定的动态实例化一个类name: String
(以下)。这适用于简单的情况 - 类和具有一个构造函数的案例类。
我不确定如何处理存在多个构造函数的一般情况。我认为它们只能通过它们的签名来区分,但发现伴生对象、类和案例类之间的概念交互有点难以推理。需要考虑的所有“构建”场景的完整描述是什么,或者,您将如何将其发展为完全通用?
这段代码在遍历伴生对象时令人尴尬地失败,这证明了它的不灵活性。
如果有人愿意加入这个思维练习,希望这段代码能够提供方便的测试和修补流程。
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")}""")
}
请注意,它需要这个build.sbt
或类似的:
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"
)
)