Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to recognize scala constructor parameter 'fields' with no underlying java field?

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?

like image 571
Turin Avatar asked Jun 21 '13 20:06

Turin


People also ask

Can Scala object have constructor?

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.

What is auxiliary constructor in Scala?

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.

What is primary constructor in Scala?

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.

When a class is instantiated its primary constructor is?

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.


2 Answers

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)
like image 177
kiritsuku Avatar answered Sep 26 '22 05:09

kiritsuku


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 = " "
like image 21
som-snytt Avatar answered Sep 22 '22 05:09

som-snytt