In Scala Reflection, why do constructor options hide getters?

var in the scala class automatically gets getters and setters, which you can see through scala reflection through members

 import scala.reflect.runtime.{universe => ru} class A(var x: Int) scala> ru.typeOf[A].members.filter{_.name.toString.contains("x")} res22: Iterable[reflect.runtime.universe.Symbol] = SynchronizedOps(variable x, method x_=, method x) 

However, if you create a subclass that reuses the var name in the constructor, getter will not:

 class B(x:Int, var y: Int) extends A(x) scala> ru.typeOf[B].members.filter{_.name.toString.contains("x")} res23: Iterable[reflect.runtime.universe.Symbol] = SynchronizedOps(value x, method x_=) scala> res23.head.asTerm.isVal res25: Boolean = true 

This seems a bit wrong ... because B still has a getter for x (and its not a val )

 scala> val b = new B(5,6) b: B = B@270288ed scala> bx res26: Int = 5 scala> bx = 7 bx: Int = 7 scala> bx res27: Int = 7 

If I try to pretend that the value x I got from members is getter, I get an error:

 scala> val xGetter = res23.head.asTerm xGetter: reflect.runtime.universe.TermSymbol = value x scala> val objMirror = ru.runtimeMirror(getClass.getClassLoader).reflect(b) objMirror: reflect.runtime.universe.InstanceMirror = instance mirror for B@270288ed scala> val getterMirror = objMirror.reflectField(xGetter) scala.ScalaReflectionException: Scala field x 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. 

What is the right workaround here? Is it wrong to have a subclass name, its args constructor matches the names in the parent arguments? Or instead of calling members , do I need to work on all superclasses to get all getters and setters?

Note that members gives me an inherited getter until a subclass creates a constructor with the same name:

 class Y(var x: Int) class Z(q:Int, z: Int) extends Y(q) scala> ru.typeOf[Z].members.filter{_.name.toString.contains("x")} res28: Iterable[reflect.runtime.universe.Symbol] = SynchronizedOps(method x_=, method x) 

EDIT If it is unclear, I really ask: 1) is this an error in scala reflection? 2) if not, I should: (a) never have classes that use constructor field names the same as field names in base classes? (if so, I'm probably defining all my classes incorrectly ...) or (b) getting all getters and setters, should I just look at the list of all the parent classes and use declarations , instead of relying on members to do the right thing because it does not work in this case

EDIT 2 in response to @ som-snytt's answer, the visible methods of x really are on x in A , and not in the parameter in constructor B For instance:.

 class A(var x: Int){def showMeX {println(x)}} class B(x:Int, var y: Int) extends A(x) scala> val b = new B(5,10) scala> b.showMeX 5 scala> bx = 17 bx: Int = 17 scala> b.showMeX 17 

so I don’t think that either the getter or setter for x been obscured in terms of normal user code. It was only shaded for the reflection code ... and it makes no sense to me that there will be two different versions of the shading.

+6
source share
3 answers

2) if not, should I: (a) never use classes, do the names of the constructors match the names of the fields in the base classes?

Since they did not allow me to fix it, this is exactly what I am doing. I try to give all constructor parameters new names that are different from all inherited names. Here is a typical example in the compiler.

 class PackageClassSymbol protected[Symbols] (owner0: Symbol, pos0: Position, name0: TypeName) 

Yes, this is ridiculous.

Oh boy don't keep pulling that thread ...

It is easy to see that the probability that it will be considered is zero. This is a great example of why I left.

By the way, if you use -Xlint, this warns you about this. This was mentioned in SI-4762.

 % cat a.scala class A(var x: Int) class B(x:Int, var y: Int) extends A(x) { def z = x } % scalac -Xlint a.scala a.scala:3: warning: private[this] value x in class B shadows mutable x inherited from class A. Changes to x will not be visible within class B - you may want to give them distinct names. def z = x ^ one warning found 
+6
source

Well in:

 class B(x:Int, var y: Int) extends A(x) 

x in B is private (not a case specifier, no val or var ), x in is public (you specified var ). I'm not too familiar with this reflection API, but does it show private members?

If you instead:

 scala> class B(val x:Int, var y: Int) extends A(10) <console>:9: error: overriding variable x in class A of type Int; value x needs `override' modifier class B(val x:Int, var y: Int) extends A(10) ^ 

The only public x is what is indicated in A, which can be shown here:

 scala> class B(x:Int, var y: Int) extends A(10) defined class B scala> new B(2,3).x res4: Int = 10 

If you want to override the parent, use override and change the parent to something that can be overridden.

0
source

I guess that until the answer to my question is (2a), then if someone else comes across this, here is a workaround. Perhaps this will not be necessary in the future, depending on the answer to (1).

(Some additional things here, but maybe also useful)

 import scala.reflect.runtime.{universe => ru} object ReflectionUtils { def extractGetterSetterPairs(typ: ru.Type): Seq[GetterSetterPair] = { typ.baseClasses.foldLeft(Seq[GetterSetterPair]()){case (acc, clsSymb) => extractGetterSetterPairs(clsSymb.asClass.toType, acc) } } private def extractGetterSetterPairs(typ: ru.Type, acc: Seq[GetterSetterPair]): Seq[GetterSetterPair] = { val terms = typ.declarations.collect{case x if x.isTerm => x.asTerm} acc ++ terms.filter{x => x.isGetter}.map{x => x -> x.setter}. filter{case(g,s) => s.isTerm}.map{case(g,s) => GetterSetterPair(g,s.asTerm) } } def termName(t: ru.TermSymbol): String = { t.name.toString.trim } } case class GetterSetterPair(getter: ru.TermSymbol, setter: ru.TermSymbol) { val name = ReflectionUtils.termName(getter) val fieldType = { //this is way more complicated than it should be. But // 1) getters for some reason are not instances of ru.MethodType // java.lang.ClassCastException: scala.reflect.internal.Types$NullaryMethodType cannot be cast to scala.reflect.api.Types$MethodTypeApi // 2) its a headache to get the types out of setters val m = setter.typeSignature. asInstanceOf[ru.MethodType] m.params.head.typeSignature } } 
0
source

All Articles