1

我需要能够通过反射来实例化各种案例类,既要弄清楚构造函数的参数类型,又要使用所有默认参数调用构造函数。

我已经做到了:

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)

现在缺少的部分:

val p2 = ctorm()  // IllegalArgumentException: wrong number of arguments

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

4

4 回答 4

4

所以在链接的问题中,: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

这真的是最短路径吗?

于 2013-06-05T12:48:42.570 回答
3

没有最小化……也没有认可……

scala> import scala.reflect.runtime.universe
import scala.reflect.runtime.universe

scala> import scala.reflect.internal.{ Definitions, SymbolTable, StdNames }
import scala.reflect.internal.{Definitions, SymbolTable, StdNames}

scala> val ds = universe.asInstanceOf[Definitions with SymbolTable with StdNames]
ds: scala.reflect.internal.Definitions with scala.reflect.internal.SymbolTable with scala.reflect.internal.StdNames = scala.reflect.runtime.JavaUniverse@52a16a10

scala> val n = ds.newTermName("foo")
n: ds.TermName = foo

scala> ds.nme.defaultGetterName(n,1)
res1: ds.TermName = foo$default$1
于 2013-06-05T14:47:38.183 回答
0

这是如何使用默认构造函数参数( Github 源)通过反射创建案例类的最完整示例:

import scala.reflect.runtime.universe
import scala.reflect.internal.{Definitions, SymbolTable, StdNames}

object Main {
  def newInstanceWithDefaultParameters(className: String): Any = {
    val runtimeMirror: universe.Mirror = universe.runtimeMirror(getClass.getClassLoader)
    val ds = universe.asInstanceOf[Definitions with SymbolTable with StdNames]
    val classSymbol = runtimeMirror.staticClass(className)
    val classMirror = runtimeMirror.reflectClass(classSymbol)
    val moduleSymbol = runtimeMirror.staticModule(className)
    val moduleMirror = runtimeMirror.reflectModule(moduleSymbol)
    val moduleInstanceMirror = runtimeMirror.reflect(moduleMirror.instance)
    val defaultValueMethodSymbols = moduleMirror.symbol.info.members
      .filter(_.name.toString.startsWith(ds.nme.defaultGetterName(ds.newTermName("apply"), 1).toString.dropRight(1)))
      .toSeq
      .reverse
      .map(_.asMethod)
    val defaultValueMethods = defaultValueMethodSymbols.map(moduleInstanceMirror.reflectMethod).toList
    val primaryConstructorMirror = classMirror.reflectConstructor(classSymbol.primaryConstructor.asMethod)
    primaryConstructorMirror.apply(defaultValueMethods.map(_.apply()): _*)
  }

  def main(args: Array[String]): Unit = {
    val instance = newInstanceWithDefaultParameters(classOf[Bar].getName)
    println(instance)
  }
}

case class Bar(i: Int = 33)
于 2019-03-10T15:36:56.683 回答
0

这是一个可以复制到代码库中的工作版本:

import scala.reflect.api
import scala.reflect.api.{TypeCreator, Universe}
import scala.reflect.runtime.universe._

object Maker {
  val mirror = runtimeMirror(getClass.getClassLoader)

  var makerRunNumber = 1

  def apply[T: TypeTag]: T = {
    val method = typeOf[T].companion.decl(TermName("apply")).asMethod
    val params = method.paramLists.head
    val args = params.map { param =>
      makerRunNumber += 1
      param.info match {
        case t if t <:< typeOf[Enumeration#Value] => chooseEnumValue(convert(t).asInstanceOf[TypeTag[_ <: Enumeration]])
        case t if t =:= typeOf[Int] => makerRunNumber
        case t if t =:= typeOf[Long] => makerRunNumber
        case t if t =:= typeOf[Date] => new Date(Time.now.inMillis)
        case t if t <:< typeOf[Option[_]] => None
        case t if t =:= typeOf[String] && param.name.decodedName.toString.toLowerCase.contains("email") => s"random-$arbitrary@give.asia"
        case t if t =:= typeOf[String] => s"arbitrary-$makerRunNumber"
        case t if t =:= typeOf[Boolean] => false
        case t if t <:< typeOf[Seq[_]] => List.empty
        case t if t <:< typeOf[Map[_, _]] => Map.empty
        // Add more special cases here.
        case t if isCaseClass(t) => apply(convert(t))
        case t => throw new Exception(s"Maker doesn't support generating $t")
      }
    }

    val obj = mirror.reflectModule(typeOf[T].typeSymbol.companion.asModule).instance
    mirror.reflect(obj).reflectMethod(method)(args:_*).asInstanceOf[T]
  }

  def chooseEnumValue[E <: Enumeration: TypeTag]: E#Value = {
    val parentType = typeOf[E].asInstanceOf[TypeRef].pre
    val valuesMethod = parentType.baseType(typeOf[Enumeration].typeSymbol).decl(TermName("values")).asMethod
    val obj = mirror.reflectModule(parentType.termSymbol.asModule).instance

    mirror.reflect(obj).reflectMethod(valuesMethod)().asInstanceOf[E#ValueSet].head
  }

  def convert(tpe: Type): TypeTag[_] = {
    TypeTag.apply(
      runtimeMirror(getClass.getClassLoader),
      new TypeCreator {
        override def apply[U <: Universe with Singleton](m: api.Mirror[U]) = {
          tpe.asInstanceOf[U # Type]
        }
      }
    )
  }

  def isCaseClass(t: Type) = {
    t.companion.decls.exists(_.name.decodedName.toString == "apply") &&
      t.decls.exists(_.name.decodedName.toString == "copy")
  }
}

而且,当你想使用它时,你可以调用:

val user = Maker[User]
val user2 = Maker[User].copy(email = "someemail@email.com")

上面的代码生成任意且唯一的值。数据并不是完全随机的。最好在测试中使用。

它适用于枚举和嵌套案例类。您还可以轻松扩展它以支持其他一些特殊类型。

在此处阅读我们的完整博客文章:https ://give.engineering/2018/08/24/instantiate-case-class-with-arbitrary-value.html

于 2018-08-25T23:09:14.010 回答