如何通过 Scala 反射访问默认参数值? [英] How do I access default parameter values via Scala reflection?

查看:30
本文介绍了如何通过 Scala 反射访问默认参数值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一堂课:

case class Foo(id: Int, name: String, note: Option[String] = None)

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

Both the constructor and the apply method in the automatically generated companion object take three parameters. When viewed via reflection, the third parameter (note) is flagged:

p.isParamWithDefault = true

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

Also, by inspection I can find the method that produces the value in the companion object:

method <init>$default$3

method apply$default$3

两者都有:

m.isParamWithDefault = true

但是,我既无法在 Notes 参数的 TermSymbol 上找到任何实际指向我获取默认值的正确方法的内容,也无法在上述 MethodSymbol 上找到任何指向参数的 TermSymbol 的内容.

However, I can neither find anything on the TermSymbol for the notes parameter that actually points me at the right methods to obtain the default value nor anything on the above MethodSymbols that point back to the TermSymbol for the parameter.

是否有一种直接的方法可以将参数的 TermSymbol 与生成其默认值的方法联系起来?或者我是否需要做一些笨拙的事情,例如检查伴随对象上方法的名称?

Is there a straight forward way to link TermSymbol for the parameter with the method that generates its default value? Or do I need to do something kludgey like inspect the names of the methods on the companion object?

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

I'm interested in this both for the case class constructor example I have here and for regular methods.

推荐答案

有一定程度的混乱.

此答案中的示例代码,粘贴在下方.

Sample code at this answer, pasted below.

正如我所说,名称的形式在 4.6、6.6.1 的规范中.这不是临时的.对于每个带有默认参数的参数 pi , j 都会生成一个名为 f $default$n 的方法,该方法计算默认参数表达式.

So as I was saying, the form of the name is in the spec at 4.6, 6.6.1. That is not ad-hoc. For every parameter pi , j with a default argument a method named f $default$n is generated which computes the default argument expression.

缺乏访问和重组这些生成名称的结构化能力是一个已知问题(ML 上的当前线程).

The lack of structured ability to access and reconstitute these generated names is a known issue (with a current thread on the 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]())
}

这篇关于如何通过 Scala 反射访问默认参数值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆