使用 Scala 反射获取构造函数参数值

Dar*_*key 0 reflection scala scala-macros scala-reflect

在带有 Scala 的代码库中工作,它希望您为某些类定义一种“制作新版本” - 例如,如果您有一个类 x(a :int, b:String, c:double).. . 它会有这样的功能:

class x( a: Integer, b : String, c : Double) extends CanMakeNew
{
    def newValue() = x( a, b, c)
}
Run Code Online (Sandbox Code Playgroud)

我无法控制它 - 但不想每次都实现它。或者,嗯……永远。在 Scala 中有一种方法可以通过反射来迭代构造函数参数值吗?我可以使用反射来查看参数类型- 但由于该模块的参数名称尚未打开,我无法打开它 - 我无法将这些与类中存储的值相关联。从根本上说,我正在寻找实现以下特征的方法:

trait CanMakeNewDoneForMe extends CanMakeNew {
    def newValue()  {I need the code that goes here}
Run Code Online (Sandbox Code Playgroud)

那么scala反射有没有办法检查构造函数或检查对象并看到“啊,这是构造函数中的第三个参数”?

Dmy*_*tin 5

如果您创建X一个 case 类,它将具有apply, copy... 由编译器自动生成。

基本上它不是我的代码库,所以我无法真正改变任何事物的形状......

当您将类设为案例类时,您实际上并没有“改变事物的形状”,您只需添加自动生成的方法。

无论如何,您可以创建一个生成方法的宏注释newValue

  import scala.annotation.StaticAnnotation
  import scala.language.experimental.macros
  import scala.reflect.macros.blackbox

  class newValue extends StaticAnnotation {
    def macroTransform(annottees: Any*): Any = macro newValueMacro.impl
  }

  object newValueMacro {
    def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
      import c.universe._
      annottees match {
        case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
          val tparams1 = tparams.map {
            case q"$_ type $name[..$_] >: $_ <: $_" => tq"$name"
          }
          val paramss1 = paramss.map(_.map {
            case q"$_ val $pat: $_ = $_" => pat
          })
          q"""
              $mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self =>
                def newValue() = new $tpname[..$tparams1](...$paramss1)
                ..$stats
              }

              ..$tail
            """

        case _ => c.abort(c.enclosingPosition, "not a class")
      }

    }
  }

  @newValue
  /*case*/ class X(a: Int, b : String, c : Double) {
    override def toString: String = s"X($a, $b, $c)"
  }

  val x = new X(1, "a", 2.0) //X(1, a, 2.0)
  //  val x1 = x.copy()
  val x1 = x.newValue() //X(1, a, 2.0)
Run Code Online (Sandbox Code Playgroud)