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

コードベースにコピーできる作業バージョンを次に示します。

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")

上記のコードは、任意の一意の値を生成します。データは正確にランダム化されていません。テストでの使用に最適です。

Enum およびネストされたケース クラスで動作します。他の特殊なタイプをサポートするように簡単に拡張することもできます。

こちらのブログ投稿全文をお読みください: https://give.engineering/2018/08/24/instantiate-case-class-with-arbitrary-value.html

于 2018-08-25T23:09:14.010 に答える