PHP introduces a method that allows you to pick out all public values of an instance. Is there any way to do this in Scala? That is to fetch all values of all public fields
Scala deliberately makes val
, var
and def
share a common interface, so you can replace the former two with the latter without breaking any code -- without even requiring a recompile.
So, while it is possible to do what you want, it would result in brittle code, that does the sort of thing it shouldn't do.
It's time for bed, so I don't have time for a full answer, but look at the results of element.getClass.getFields
(or getDeclaredFields
for private fields) - you can call getValue(element)
on the Field
objects to fetch their values.
Awake now, and still no better answer, so:
First, note that in Java terms, your class doesn't have a public field subject, what it has is a private field subject and accessor methods subject() and subject_$eq(String).
You can iterate over the private field objects as described above, populating a Map from the pairs:
def getFields(o: Any): Map[String, Any] = {
val fieldsAsPairs = for (field <- o.getClass.getDeclaredFields) yield {
field.setAccessible(true)
(field.getName, field.get(o))
}
Map(fieldsAsPairs :_*)
}
Now you can either define this method on TestElement (replacing o
with this
), or more generally usefully define a conversion so that you can call getFields on any reference
implicit def any2FieldValues[A](o: A) = new AnyRef {
def fieldValues = getFields(o)
}
So that
element.fieldValues
will give the result you want.
Just a note to those who try to improve this by making @duncan 's approach type-stronger:
Instead of returning a Map[String, Any]
, where the value is typed as Any, you could do following:
def propertiesAsPairs() = {
val fields = (this.getClass.getDeclaredFields())
for ( field <- fields ) yield {
field.setAccessible( true );
( field.getName, field.get( this ) );
}
}
You can do something relatively close to this for case classes:
case class SomeEntity(name : String, value : Int, misc : Boolean)
val s = SomeEntity("Tom", 42, false)
println(s.productIterator.map(_.toString).mkString(", ")) // "Tom, 42, false"
...as you would expect, productIterator iterates over elements of type Any. This method is automatically generated for case classes only, and you will not retrieve the name of the field. For anything more, you will need to use reflection, and for that, you may want to wait for 2.10 to come out.
A per Philippe's answer, you can do this for case classes.
More broadly though, the same technique works for any subclass of Product
. As well as case classes, Tuples are another obvious example, but the list is far more extensive than that.
Take a look at the "known subclasses", here: http://www.scala-lang.org/api/current/scala/Product.html