如何通过Scala反射访问默认参数值?

Eri*_*cht 14 reflection scala scala-2.10

让我们说一个班级:

case class Foo(id: Int, name: String, note: Option[String] = None)
Run Code Online (Sandbox Code Playgroud)

自动生成的伴随对象中的构造函数和apply方法都有三个参数.通过反射查看时,标记了第三个参数(注释):

p.isParamWithDefault = true
Run Code Online (Sandbox Code Playgroud)

此外,通过检查,我可以找到在伴随对象中生成值的方法:

method <init>$default$3
Run Code Online (Sandbox Code Playgroud)

method apply$default$3
Run Code Online (Sandbox Code Playgroud)

哪两个都有:

m.isParamWithDefault = true
Run Code Online (Sandbox Code Playgroud)

但是,我无法在TermSymbol上找到任何关于notes参数的内容,它实际上指向了正确的方法来获取默认值,也没有找到上述MethodSymbols中指向参数的TermSymbol的任何内容.

是否有直接的方法将参数的TermSymbol与生成其默认值的方法相关联?或者我是否需要做一些像检查伴侣对象上的方法名称一样的事情?

我对这里的case类构造函数示例和常规方法感兴趣.

som*_*ytt 9

有一定程度的kludge.

此答案的示例代码粘贴在下面.

正如我所说,名称的形式在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]())
}
Run Code Online (Sandbox Code Playgroud)