12

特定のデバッグ目的で、実行時にフィールドを検査できるように、ケース クラスに混在できる (Scala 2.8 の) トレイトを作成しようとしています。ソース ファイルで宣言された順序に戻して、ケース クラス内の他のフィールドを省略したいと考えています。例えば:

trait CaseClassReflector extends Product {

  def getFields: List[(String, Any)] = {
    var fieldValueToName: Map[Any, String] = Map()
    for (field <- getClass.getDeclaredFields) {
      field.setAccessible(true)
      fieldValueToName += (field.get(this) -> field.getName) 
    }
    productIterator.toList map { value => fieldValueToName(value) -> value }
  }

}

case class Colour(red: Int, green: Int, blue: Int) extends CaseClassReflector {
  val other: Int = 42
}

scala> val c = Colour(234, 123, 23)
c: Colour = Colour(234,123,23)

scala> val fields = c.getFields    
fields: List[(String, Any)] = List((red,234), (green,123), (blue,23))

上記の実装は、Product 内のフィールドの位置とその名前の間の関係をそれらのフィールドの値の等価性によって推測するため、明らかに欠陥があります。

Colour(0, 0, 0).getFields

これを実装する方法はありますか?

4

4 に答える 4

10

トランクを調べると、これが見つかります。コメントを聞いてください。これはサポートされていません。しかし、それらの名前も必要だったので...

/** private[scala] so nobody gets the idea this is a supported interface.
 */
private[scala] def caseParamNames(path: String): Option[List[String]] = {
  val (outer, inner) = (path indexOf '$') match {
    case -1   => (path, "")
    case x    => (path take x, path drop (x + 1))
  }

  for {
    clazz <- getSystemLoader.tryToLoadClass[AnyRef](outer)
    ssig <- ScalaSigParser.parse(clazz)
  }
  yield {
    val f: PartialFunction[Symbol, List[String]] =
      if (inner.isEmpty) {
        case x: MethodSymbol if x.isCaseAccessor && (x.name endsWith " ") => List(x.name dropRight 1)
      }
      else {
        case x: ClassSymbol if x.name == inner  =>
          val xs = x.children filter (child => child.isCaseAccessor && (child.name endsWith " "))
          xs.toList map (_.name dropRight 1)
      }

    (ssig.symbols partialMap f).flatten toList
  }
}
于 2010-02-11T14:22:06.663 に答える
9

上記の例に基づいた、短くて実用的なバージョンを次に示します。

  trait CaseClassReflector extends Product {
    def getFields = getClass.getDeclaredFields.map(field => {
      field setAccessible true
      field.getName -> field.get(this)
    })
  }
于 2013-03-27T10:31:38.493 に答える
7

すべての例で、フィールドの順序が逆になっていることを確認しました。getFields配列の最後の項目は、caseクラスにリストされている最初の項目です。productElement(n)ケースクラスを「うまく」使用する場合は、にマップできるはずですgetDeclaredFields()( getDeclaredFields.length-n-1)

しかし、これはかなり危険です。仕様には、そのようにする必要があると主張するものは何もありません。caseクラスでvalをオーバーライドすると、getDeclaredFieldsにも表示されません(表示されます)。そのスーパークラスの分野で)。

コードを変更して、このようになっていると想定することもできますが、その名前のgetterメソッドとproductIteratorが同じ値を返し、そうでない場合は例外をスローすることを確認してください(つまり、何が対応するのか実際にはわかりません)。何)。

于 2010-02-08T20:35:04.700 に答える
4

ProductCompletionインタープリター パッケージの を使用して、ケース クラスの属性名と値を取得することもできます。

import tools.nsc.interpreter.ProductCompletion

// get attribute names
new ProductCompletion(Colour(1, 2, 3)).caseNames
// returns: List(red, green, blue)

// get attribute values
new ProductCompletion(Colour(1, 2, 3)).caseFields

編集:rolandとvirtualeyesによるヒント

scala-lang コレクションscalapの一部であるライブラリを含める必要があります。

ヒントをありがとう、roland と virtualeyes。

于 2011-09-06T13:04:35.567 に答える