Question

Je suis en train d'écrire un trait (à Scala 2.8) qui peuvent être mélangés dans une classe de cas, ce qui permet à ses champs à contrôler à l'exécution, à des fins de débogage particulier. Je veux les récupérer dans l'ordre qu'ils ont été déclarés dans le fichier source, et je voudrais omettre tous les autres champs à l'intérieur de la classe affaire. Par exemple:

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))

La mise en œuvre ci-dessus est clairement erronée, car elle devine la relation entre la position d'un champ dans le produit et son nom par l'égalité de la valeur sur les champs, de sorte que ce qui suit, par exemple, ne fonctionne pas:

Colour(0, 0, 0).getFields

Est-il possible, cela peut être mis en œuvre?

Était-ce utile?

La solution

Dans tous les exemples que j'ai vu les champs sont dans l'ordre inverse: le dernier élément du tableau getFields est le premier répertorié dans la classe de cas. Si vous utilisez des classes de cas « bien », alors vous devriez juste pouvoir cartographier productElement(n) sur getDeclaredFields()( getDeclaredFields.length-n-1).

Mais cela est assez dangereux, car je ne sais pas quoi que ce soit de la spécification qui insiste sur le fait qu'il doit être de cette façon, et si vous remplacez un val dans la classe de cas, il apparaît même dans getDeclaredFields (il apparaîtrez dans les domaines de cette superclasse).

Vous pouvez changer votre code pour prendre les choses sont ainsi, mais il faut vérifier que la méthode de lecture avec ce nom et le productIterator retournent la même valeur et jeter une exception si elles ne le font pas (ce qui signifie que vous ne savez pas vraiment ce qui correspond à ce que).

Autres conseils

Regardez dans le coffre et vous trouverez cela. Écoutez le commentaire, ce ne sont pas pris en charge: mais depuis que je devais aussi ces noms ...

/** 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
  }
}

Voici une version courte et travail, basée sur l'exemple ci-dessus

  trait CaseClassReflector extends Product {
    def getFields = getClass.getDeclaredFields.map(field => {
      field setAccessible true
      field.getName -> field.get(this)
    })
  }

Vous pouvez également utiliser le ProductCompletion du paquet d'interprète pour se rendre à attribuer les noms et les valeurs des classes de cas:

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

Modifier: conseils de roland et virtualeyes

Il est nécessaire d'inclure la bibliothèque scalap qui fait partie de la

scroll top