在Scala Reflection中,为什么构造函数params会隐藏getter?

时间:2013-12-19 22:12:51

标签: scala reflection

scala类中的

var会自动获取getter&你可以通过members

通过scala反射看到的setter
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都被遮蔽了。它只被反射代码遮蔽了......对我来说没有任何意义,它会有两种不同版本的阴影。

3 个答案:

答案 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是私有的(不是案例类,没有valvar说明符),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
  }
}