Pergunta

Eu estou tentando escrever um traço (em Scala 2.8) que pode ser misturado para uma classe caso, permitindo que os seus campos a serem inspecionados em tempo de execução, para um propósito depuração particular. Eu quero recuperá-los na ordem em que eles foram declarados no arquivo de origem, e eu gostaria de omitir qualquer outro campo dentro da classe caso. Por exemplo:

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

A implementação acima é claramente deficiente porque adivinha a relação entre a posição de um campo no Produto e seu nome pela igualdade do valor sobre aqueles campo, de modo que o seguinte, por exemplo, não vai funcionar:

Colour(0, 0, 0).getFields

Existe alguma maneira isso pode ser implementado?

Foi útil?

Solução

Em todos os exemplos que eu vi os campos estão em ordem inversa: o último item na matriz getFields é o primeiro listado na classe caso. Se você usar as classes de caso "muito bem", então você deve apenas ser capaz de mapear productElement(n) para getDeclaredFields()( getDeclaredFields.length-n-1).

Mas isso é bastante perigoso, como eu não sei de nada na especificação que insiste que ele deve ser dessa maneira, e se você substituir um val na classe caso, não vai mesmo aparecer em getDeclaredFields (-lo vai aparecer nos campos do que superclasse).

Você pode alterar o código para assumir as coisas são assim, mas verificar se o método getter com esse nome eo productIterator retornar o mesmo valor e lançar uma exceção se não (o que significa que você realmente não sabe fazer o que corresponde ao que).

Outras dicas

Procurar em tronco e você vai encontrar isso. Ouça o comentário, este não é suportado, mas desde que eu também precisava esses nomes ...

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

Aqui está uma versão curta e de trabalho, com base no exemplo acima

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

Você também pode usar o ProductCompletion do pacote de intérprete para chegar a nomes e valores das classes de caso de atributos:

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

Editar: dicas de roland e virtualeyes

É necessário incluir a biblioteca scalap que faz parte do scala-lang coleção .

Obrigado por seus sugestões, roland e virtualeyes.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top