I'm playing with reflection in scala. I chose
class CloneSubject(val a :Int, b :String, private var c :Boolean)
as a subject and iterate over typeOf[CloneSubject].members
to find fields (sym.isTerm && (sym.asTerm.isVar || sym.asTerm.isVal)
) but when I try to create a FieldMirror, (cm mirrorField sym.asTerm
) I get this exception:
Exception in thread "main" scala.ScalaReflectionException: Scala field b isn't represented as a Java field,
neither it has a Java accessor method
note that private parameters of class constructors don't get mapped onto fields and/or accessors,
unless they are used outside of their declaring constructors.`
Now, I understand perfectly why this happens, but I cannot find a way to check for this without throwing an exception. Of course, using Java reflection works just fine, but that's against the point. Any ideas?
Constructors in Scala describe special methods used to initialize objects. When an object of that class needs to be created, it calls the constructor of the class. It can be used to set initial or default values for object attributes.
The auxiliary constructor in Scala is used for constructor overloading and defined as a method using this name. The auxiliary constructor must call either previously defined auxiliary constructor or primary constructor in the first line of its body.
The primary constructor of a Scala class is a combination of: The constructor parameters. Methods that are called in the body of the class. Statements and expressions that are executed in the body of the class.
The primary constructor consists of the constructor parameters, the methods called in the class body, and the statements executed in the body of the class. This time, instead of a no-argument constructor provided by Scala, this class has a two-argument constructor we defined by listing them next to the class name.
To avoid the error what about filtering out b
beforehand:
scala> val m = reflect.runtime.universe.runtimeMirror(getClass.getClassLoader)
m: reflect.runtime.universe.Mirror = JavaMirror with ...
scala> val im = m.reflect(new CloneSubject(5, "hello", true))
im: reflect.runtime.universe.InstanceMirror = instance mirror for CloneSubject@2a95e173
scala> val fields = typeOf[CloneSubject].members collect { case m: MethodSymbol if m.isGetter => m.accessed }
fields: Iterable[reflect.runtime.universe.Symbol] = List(variable c, value a)
scala> fields map (s => im.reflectField(s.asTerm).get)
res45: Iterable[Any] = List(true, 5)
You know that every field that can be accessed have a Getter, thus the only thing to do is to get the field represented by all existing Getters.
Another option to use it to check if a val/var has a Getter:
scala> typeOf[CloneSubject].members.filter(s => s.isTerm && (s.asTerm.isVar || s.asTerm.isVal) && s.asTerm.getter != NoSymbol)
res50: Iterable[reflect.runtime.universe.Symbol] = SynchronizedOps(variable c, value a)
To write in invisible ink you can use lemon juice:
scala> typeOf[CloneSubject].members.collect { case s if s.name.decoded.endsWith(" ") => s }
res9: Iterable[reflect.runtime.universe.Symbol] = List(variable c, value a)
scala> cm reflect new CloneSubject(8, "foo", true)
res11: reflect.runtime.universe.InstanceMirror = instance mirror for CloneSubject@cdeca29
scala> res9 map (res11 reflectField _.asTerm) map (_.get)
res12: Iterable[Any] = List(true, 8)
The mysterious space is:
scala> import reflect.runtime.universe._
import reflect.runtime.universe._
scala> nme.
CONSTRUCTOR EMPTY EMPTY_PACKAGE_NAME ERROR
LOCAL_SUFFIX_STRING NameType PACKAGE ROOTPKG
WILDCARD asInstanceOf isInstanceOf toString
scala> nme.LOCAL_SUFFIX_STRING
res0: String = " "
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With