var
会自动获取getter&你可以通过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)
但是,如果您创建一个子类,它在构造函数中重用了var名称,那么getter就会消失:
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
这似乎有点误导......毕竟,B
仍有x
的吸气剂(而不是val
)
scala> val b = new B(5,6)
b: B = B@270288ed
scala> b.x
res26: Int = 5
scala> b.x = 7
b.x: Int = 7
scala> b.x
res27: Int = 7
如果我试图假装我从value x
得到的members
是吸气剂,我会收到错误:
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.
这里的解决方法是什么?具有子类名称,其构造函数args与父args中的名称相同是完全错误的吗?或者不是打电话给members
,我是否需要让我的所有超级课程都能让所有的课程得到充分的帮助。设置器?
请注意,members
只要子类不创建具有相同名称的构造函数,就会为我提供继承的getter:
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)
修改
如果不清楚,我真的在问:
1)这是scala反射中的一个错误吗?
2)如果没有,我应该:
(a)从来没有类使用构造函数字段的名称与基类中的字段名称相同? (如果是这样,我可能会错误地定义我的所有课程...)
要么
(b)获得所有吸气剂和setters,我应该只浏览所有父类的列表并使用declarations
,而不是依靠members
来做正确的事情,因为它在这种情况下不起作用吗?
编辑2
为了回应@ som-snytt的回答,x
的可见方法实际上位于x
中的A
,而不是B
的构造函数中的参数。例如:
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> b.x = 17
b.x: Int = 17
scala> b.showMeX
17
所以我并不认为从普通用户代码的角度来看,x
的getter或setter都被遮蔽了。它只被反射代码遮蔽了......对我来说没有任何意义,它会有两种不同版本的阴影。
答案 0 :(得分:6)
2)如果没有,我应该:(a)从来没有类使用构造函数字段的名称与基类中的字段名称相同?
因为他们不会让我解决这个问题,这正是我所做的。我尝试给所有构造函数参数赋予与所有继承名称不同的新名称。这是编译器中的典型示例。
class PackageClassSymbol protected[Symbols] (owner0: Symbol, pos0: Position, name0: TypeName)
是的,这太荒谬了。
哦,小伙子,不要继续拉那条线......
不难发现任何一个被解决的几率为零。这是我退出原因的完美例子。
顺便说一句,如果您使用-Xlint,它会向您发出警告。这在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
答案 1 :(得分:0)
嗯,在:
class B(x:Int, var y: Int) extends A(x)
x
中的B
是私有的(不是案例类,没有val
或var
说明符),A中的x是公开的(您指定{{1} }})。我对这个反射API不太熟悉,但它是否显示私有成员?
如果您改为:
var
唯一的公共x是A中的一个,可以在这里显示:
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)
^
如果要覆盖父成员,请使用scala> class B(x:Int, var y: Int) extends A(10)
defined class B
scala> new B(2,3).x
res4: Int = 10
,并将父级更改为可以覆盖的内容。
答案 2 :(得分:0)
我想只要我的问题的答案不是(2a),那么如果有其他人碰到这个,这是一个解决方法。根据(1)的答案,将来可能没有必要。
(这里有一些额外的东西,但也许有用)
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
}
}