19

私はクラスを持っているとしましょう:

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

自動生成されたコンパニオン オブジェクトのコンストラクターと apply メソッドの両方が 3 つのパラメーターを受け取ります。リフレクションを介して表示すると、3 番目のパラメーター (メモ) にフラグが立てられます。

p.isParamWithDefault = true

また、調べると、コンパニオン オブジェクトで値を生成するメソッドを見つけることができます。

method <init>$default$3

method apply$default$3

どちらも持っています:

m.isParamWithDefault = true

ただし、デフォルト値を取得するための適切なメソッドを実際に指摘する notes パラメーターの TermSymbol も、パラメーターの TermSymbol を指す上記の MethodSymbol も何も見つかりません。

パラメータの TermSymbol をデフォルト値を生成するメソッドにリンクする簡単な方法はありますか? または、コンパニオン オブジェクトのメソッドの名前を検査するなど、面倒なことをする必要がありますか?

ここにあるケースクラスコンストラクターの例と通常のメソッドの両方に興味があります。

4

3 に答える 3

9

応急修理の程度があります。

この回答のサンプルコードは、以下に貼り付けられています。

つまり、私が言っていたように、名前の形式は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]())
}
于 2012-12-25T23:51:15.973 に答える
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が指定されていないため、これは良い方法ではありませんが、より便利な場合があります。

于 2012-12-26T00:00:49.083 に答える