Simple Iteration over case class fields

2019-04-08 05:12发布

问题:

I'm trying to write a generic method to iterate over a case class's fields :

case class PriceMove(price: Double, delta: Double)

def log(pm : PriceMove) { info("price -> " + price + " delta -> " + delta)}

I need to make log able to handle any case class. What needs to be the argument type for log to handle case classes only and the actual generic field iteration code?

回答1:

Okay, considering the two questions I attached to the question, here is what I'd use:

object Implicits {
  implicit class CaseClassToString(c: AnyRef) {
    def toStringWithFields: String = {
      val fields = (Map[String, Any]() /: c.getClass.getDeclaredFields) { (a, f) =>
        f.setAccessible(true)
        a + (f.getName -> f.get(c))
      }

      s"${c.getClass.getName}(${fields.mkString(", ")})"
    }
  }
}

case class PriceMove(price: Double, delta: Double)

object Test extends App {
  import Implicits._
  println(PriceMove(1.23, 2.56).toStringWithFields)
}

This produces:

PriceMove(price -> 1.23, delta -> 2.56)


回答2:

I'm afraid there is no easy way to achieve what you're after, as you can't easily get the field names from the case class as discussed here: Reflection on a Scala case class and Generic customisation of case class ToString.

You can try using reflection (though you can guarantee the order of the fields) or tools.nsc.interpreter.ProductCompletion, but both solutions are significantly more complex then you'd really expect.