使用scala反射获取构造函数参数值
Get constructor parameter values using scala reflection
使用 scala 在代码库中工作,对于某些 classes,需要你定义一种 "make a new version" - 例如,如果你有一个 class 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 - 但由于此模块的参数名称尚未打开,我无法打开它 - 我无法将它们与class 中存储的值。从根本上说,我正在寻找实现如下特征的方法:
trait CanMakeNewDoneForMe extends CanMakeNew {
def newValue() {I need the code that goes here}
那么 scala 反射是否有任何方法可以检查构造函数或检查对象并查看 "ahh, this was the third parameter in the constructor"?
我在这里可能是错的,但通常这是通过使用模式匹配以及伴随对象中定义的 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
来定义 class x
(这将为您定义 apply()
和 unapply()
函数)。
如果您将 X
设为案例 class,它将有 apply
、copy
... 由编译器自动生成。
And basically it’s not my codebase, so I can’t really change any of the shape of things...
当你创建一个 class 案例时 class 你实际上并没有 "change shape of things",你只是添加了自动生成的方法。
无论如何,您可以创建一个宏注释来生成方法 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)
使用 scala 在代码库中工作,对于某些 classes,需要你定义一种 "make a new version" - 例如,如果你有一个 class 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 - 但由于此模块的参数名称尚未打开,我无法打开它 - 我无法将它们与class 中存储的值。从根本上说,我正在寻找实现如下特征的方法:
trait CanMakeNewDoneForMe extends CanMakeNew {
def newValue() {I need the code that goes here}
那么 scala 反射是否有任何方法可以检查构造函数或检查对象并查看 "ahh, this was the third parameter in the constructor"?
我在这里可能是错的,但通常这是通过使用模式匹配以及伴随对象中定义的 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
来定义 class x
(这将为您定义 apply()
和 unapply()
函数)。
如果您将 X
设为案例 class,它将有 apply
、copy
... 由编译器自动生成。
And basically it’s not my codebase, so I can’t really change any of the shape of things...
当你创建一个 class 案例时 class 你实际上并没有 "change shape of things",你只是添加了自动生成的方法。
无论如何,您可以创建一个宏注释来生成方法 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)