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

时间:2019-07-15 07:13:40

标签: scala reflection 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)
}

我对此无能为力-但宁愿不要每次都执行它。或者,嗯...曾经。在scala中,有没有一种方法可以反射-迭代构造函数的参数值?我可以使用反射来查看参数 types -但由于尚未为该模块打开参数名,因此无法将其打开-我无法将其与存储在其中的值相关联班级。从根本上讲,我正在寻找实现以下特征的方法:

trait CanMakeNewDoneForMe extends CanMakeNew {
    def newValue()  {I need the code that goes here}

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

2 个答案:

答案 0 :(得分:3)

如果将X设为case类,它将由编译器自动生成applycopy...。

  

基本上这不是我的代码库,因此我无法真正更改任何事物的形状...

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

无论如何,您可以创建一个生成方法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)

答案 1 :(得分:-1)

我在这里可能是错的,但通常这是通过模式匹配以及在您的伴随对象中定义的apply()unapply()方法来实现的。

我已经在REPL会话中对您的上述代码进行了小测试。我不了解newvalue()函数的用途,因此我跳过了它。

class x(val a: Integer, val b : String, val c : Double)
{
  //def newValue() = x( a, b, c)
}

object x {
  def apply(a: Integer, b: String, c: Double): x = new x(a,b,c)
  def unapply(m: x): Option[(Integer, String, Double)] = Some((m.a, m.b, m.c))
}

x(1, "hello", 99.0d) match {
  case l: x => println(s"this the the 3rd arg in the constructor: ${l.c}")
}

上面的unapply()函数def允许对象上的模式匹配解构。 另一种选择是使用case class定义类x(这将为您定义apply()unapply()函数)。