让我们说一个班级:
case class Foo(id: Int, name: String, note: Option[String] = None)
自动生成的伴随对象中的构造函数和apply方法都有三个参数。通过反射查看时,第三个参数(注释)被标记:
p.isParamWithDefault = true
此外,通过检查,我可以找到在伴侣对象中产生值的方法:
method <init>$default$3
和
method apply$default$3
两者都有:
m.isParamWithDefault = true
但是,我在TermSymbol上找不到任何关于notes参数的东西,它实际上指向了正确的方法来获取默认值,也没有找到上述MethodSymbols中指向参数的TermSymbol的任何内容。
是否有直接的方法将参数的TermSymbol与生成其默认值的方法相关联?或者我是否需要做一些像检查伴侣对象上的方法名称一样的事情?
我对这里的case类构造函数示例以及常规方法感兴趣。
答案 0 :(得分:9)
有一定程度的kludge。
this answer的示例代码,粘贴在下方。
正如我所说,名称的形式在4.6,6.6.1的规范中。这不是特别的。 For every parameter pi , j with a default argument a method named f $default$n is generated which computes the default argument expression.
缺乏访问和重构这些生成的名称的结构化能力是一个已知问题(ML上有当前线程)。
import reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
// case class instance with default args
// Persons entering this site must be 18 or older, so assume that
case class Person(name: String, age: Int = 18) {
require(age >= 18)
}
object Test extends App {
// Person may have some default args, or not.
// normally, must Person(name = "Guy")
// we will Person(null, 18)
def newCase[A]()(implicit t: ClassTag[A]): A = {
val claas = cm classSymbol t.runtimeClass
val modul = claas.companionSymbol.asModule
val im = cm reflect (cm reflectModule modul).instance
defaut[A](im, "apply")
}
def defaut[A](im: InstanceMirror, name: String): A = {
val at = newTermName(name)
val ts = im.symbol.typeSignature
val method = (ts member at).asMethod
// either defarg or default val for type of p
def valueFor(p: Symbol, i: Int): Any = {
val defarg = ts member newTermName(s"$name$$default$$${i+1}")
if (defarg != NoSymbol) {
println(s"default $defarg")
(im reflectMethod defarg.asMethod)()
} else {
println(s"def val for $p")
p.typeSignature match {
case t if t =:= typeOf[String] => null
case t if t =:= typeOf[Int] => 0
case x => throw new IllegalArgumentException(x.toString)
}
}
}
val args = (for (ps <- method.paramss; p <- ps) yield p).zipWithIndex map (p => valueFor(p._1,p._2))
(im reflectMethod method)(args: _*).asInstanceOf[A]
}
assert(Person(name = null) == newCase[Person]())
}
答案 1 :(得分:4)
您可以执行此操作,而无需对生成的名称进行假设 - 通过强制转换为内部API:
scala> :power
** Power User mode enabled - BEEP WHIR GYVE **
** :phase has been set to 'typer'. **
** scala.tools.nsc._ has been imported **
** global._, definitions._ also imported **
** Try :help, :vals, power.<tab> **
scala> case class Foo(id: Int, name: String, note: Option[String] = None)
defined class Foo
scala> val t = typeOf[Foo.type]
t: $r.intp.global.Type = Foo.type
scala> t.declaration(nme.defaultGetterName(nme.CONSTRUCTOR, 3))
res0: $r.intp.global.Symbol = method <init>$default$3
scala> t.declaration(nme.defaultGetterName(newTermName("apply"), 3))
res1: $r.intp.global.Symbol = method apply$default$3
当然,在某种程度上,这并不是更好,因为指定了名称修改而内部API没有,但它可能更方便。
答案 2 :(得分:1)
对于Type
中只有Foo
的情况:
% scala
Welcome to Scala 2.13.1 (OpenJDK 64-Bit Server VM, Java 15-loom).
scala> :paste
import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe.definitions._
def defaults(tpe: Type): Seq[(Int, Any)] = {
tpe.typeSymbol.asClass.primaryConstructor.asMethod.paramLists.flatten.
zipWithIndex.flatMap{ case (x, i) =>
if (x.asTerm.isParamWithDefault) {
val m = currentMirror
val im = m.reflect(
m.reflectModule(tpe.typeSymbol.asClass.companion.asModule).instance
)
val method = tpe.companion.decl(
TermName("apply$default$"+(i+1).toString)
).asMethod
val v = im.reflectMethod(method)()
Some(i -> v)
} else None
}
}
scala> case class Foo(id: Int, name: String, note: Option[String] = None)
scala> defaults(typeOf[Foo])
res0: Seq[(Int, Any)] = List((2,None))
您可以在伴随对象上调用任何方法的方式相同。
Documentation about Mirrors包含示例如何在类上调用方法。