通过反射使用默认参数实例化案例类

0__*_*0__ 1 reflection scala

我需要能够通过反射实例化各种案例类,既可以确定构造函数的参数类型,也可以使用所有默认参数调用构造函数。

我已经做到了这一点:

import reflect.runtime.{universe => ru}
val m = ru.runtimeMirror(getClass.getClassLoader)

case class Bar(i: Int = 33)

val tpe      = ru.typeOf[Bar]
val classBar = tpe.typeSymbol.asClass
val cm       = m.reflectClass(classBar)
val ctor     = tpe.declaration(ru.nme.CONSTRUCTOR).asMethod
val ctorm    = cm.reflectConstructor(ctor)

// figuring out arg types
val arg1 = ctor.paramss.head.head
arg1.typeSignature =:= ru.typeOf[Int] // true
// etc.

// instantiating with given args
val p = ctorm(33)
Run Code Online (Sandbox Code Playgroud)

现在缺少的部分:

val p2 = ctorm()  // IllegalArgumentException: wrong number of arguments
Run Code Online (Sandbox Code Playgroud)

那么我如何p2使用 的默认参数创建Bar,即没有反射的情况Bar()

0__*_*0__ 5

因此,在链接的问题中,:powerREPL 使用内部 API,这意味着它defaultGetterName不可用,因此我们需要手动构建它。采用@som-snytt 的答案:

def newDefault[A](implicit t: reflect.ClassTag[A]): A = {
  import reflect.runtime.{universe => ru, currentMirror => cm}

  val clazz  = cm.classSymbol(t.runtimeClass)
  val mod    = clazz.companionSymbol.asModule
  val im     = cm.reflect(cm.reflectModule(mod).instance)
  val ts     = im.symbol.typeSignature
  val mApply = ts.member(ru.newTermName("apply")).asMethod
  val syms   = mApply.paramss.flatten
  val args   = syms.zipWithIndex.map { case (p, i) =>
    val mDef = ts.member(ru.newTermName(s"apply$$default$$${i+1}")).asMethod
    im.reflectMethod(mDef)()
  }
  im.reflectMethod(mApply)(args: _*).asInstanceOf[A]
}

case class Foo(bar: Int = 33)

val f = newDefault[Foo]  // ok
Run Code Online (Sandbox Code Playgroud)

这真的是最短路径吗?

  • 4年过去了,还是这样吗?有什么改进吗? (2认同)