【问题标题】:Get constructor parameter values using scala reflection使用scala反射获取构造函数参数值
【发布时间】:2019-11-23 20:48:58
【问题描述】:

使用 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 反射是否有任何方法可以检查构造函数或检查对象并看到“啊,这是构造函数中的第三个参数”?

【问题讨论】:

  • newValue() 不能简单地返回this 的原因是什么? x 是否包含任何会导致返回相同实例成为问题的可变状态?
  • 是的 - x 包含大量可变状态。

标签: scala reflection scala-macros scala-reflect


【解决方案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() 函数)。

【讨论】:

  • 对不起,我不清楚。问题是 - 我如何以通用方式实现 newvalue 函数 - 这是唯一重要的事情。
  • @DarrenOakey 为什么不能将X 设为 case 类?它有applycopy...由编译器自动生成。
  • 因为我没有展示完整的图片——这些都是初始化到环境中的服务类,并且函数存在所以它们可以被初始化到另一个环境中。基本上这不是我的代码库,所以我不能真正改变任何事物的形状——我只是想找到一种不必每次都手动编写函数的聪明方法——而且我无法想象存在解决方案不涉及反射:(
【解决方案2】:

如果您将X 设为一个案例类,那么编译器会自动生成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)

【讨论】:

  • 好的 - 这很酷 - 我需要一些时间来理解它:) 谢谢
猜你喜欢
  • 2021-05-13
  • 2011-06-19
  • 1970-01-01
  • 2014-01-08
  • 1970-01-01
  • 1970-01-01
  • 2013-09-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多