문제

As a follow up of Matt R's question, as Scala 2.10 has been out for quite an amount of time, what would be the best way to extract the fields and values of a case class. Taking a similar example:

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

val RBG = Colour(1,3,"isBlue")

I want to get a list (or array or any iterator for that matter) that would have the fields declared in the constructor as tuple values like these:

[(red, 1),(green, 3),(blue, "isBlue")]

I know the fact that there are a lot of examples on the net regarding the same issue but as I said, I wanted to know what should be the most ideal way to extract the required information

도움이 되었습니까?

해결책

If you use Scala 2.10 reflection, this answer is half of the things you need. It will give you the method symbols of the case class, so you know the order and names of arguments:

import scala.reflect.runtime.{universe => ru}
import ru._

def getCaseMethods[T: TypeTag] = typeOf[T].members.collect {
  case m: MethodSymbol if m.isCaseAccessor => m
}.toList

case class Person(name: String, age: Int)

getCaseMethods[Person]  // -> List(value age, value name)

You can call .name.toString on these methods to get the corresponding method names.

The next step is to invoke these methods on a given instance. You need a runtime mirror for that

val rm = runtimeMirror(getClass.getClassLoader)

Then you can "mirror" an actual instance:

val p  = Person("foo", 33)
val pr = rm.reflect(p)

Then you can reflect on pr each method using reflectMethod and execute it via apply. Without going through each step separately, here is a solution altogether (see the val value = line for the mechanism of extracting a parameter's value):

def caseMap[T: TypeTag: reflect.ClassTag](instance: T): List[(String, Any)] = {
  val im = rm.reflect(instance)
  typeOf[T].members.collect {
    case m: MethodSymbol if m.isCaseAccessor =>
      val name  = m.name.toString
      val value = im.reflectMethod(m).apply()
      (name, value)
  } (collection.breakOut)
}

caseMap(p) // -> List(age -> 33, name -> foo)

다른 팁

Every case object is a product, therefore you can use an iterator to get all its parameters' names and another iterator to get all its parameters' values:

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

val rgb = Colour(1, 3, "isBlue")

val names = rgb.productElementNames.toList  // List(red, green, blue)
val values = rgb.productIterator.toList     // List(1, 3, isBlue)

names.zip(values).foreach(print)            // (red,1)(green,3)(blue,isBlue)

By product I mean both Cartesian product and an instance of Product. This requires Scala 2.13.0; although Product was available before, the iterator to get elements' names was only added in version 2.13.0.

Notice that no reflection is needed.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top