无法使用Scala反射获取继承的val

时间:2013-04-03 12:07:27

标签: scala reflection scala-2.10

我正在使用Scala 2.10.1,我正在尝试定义一个方法,该方法将从对象中检索所有val(包括继承的val)。

我有以下内容:

import scala.reflect.runtime.{universe => ru}

object Reflection {

  val mirror = ru.runtimeMirror(this.getClass.getClassLoader)

  def findVals(x: Any): Iterable[String] = {
    val theType = mirror.classSymbol(x.getClass).toType
    theType.members.collect({case x if x.isTerm => x.asTerm}).filter(_.isVal).map(_.name.toString)
  }

}

我正在测试这两个类:

class Base {
  val x = 10
}

class Child extends Base {
  val y = 20
}

调用以下代码时:

val x = new Child
val vs = Reflection.findVals(x)
println(vs)

结果为List(y)

出于某种原因,isVal方法会返回false对应x类中Base字段的字词。

有人可以告诉我这里的问题是什么吗?我做错了吗?

3 个答案:

答案 0 :(得分:4)

根据Why don't Scala case class fields reflect as public?,您应该使用isAccessor代替isVal

我实际上正在使用isGettersetter来根据您的评论正确过滤var

  def findVals(x: Any): Iterable[String] = {
    val theType = mirror.classSymbol(x.getClass).toType
    val xtm = theType.members.collect({case x if x.isTerm => x.asTerm})
    xtm.filter(m => m.isGetter && !xtm.exists(m.setter == _)).map(_.name.toString)
  }

结果:

scala> class Base {
     | var x = 10
     | val xx = 2
     | }
defined class Base

scala> class Child extends Base {
     | val y = 3
     | }
defined class Child

scala> val x = new Child
x: Child = Child@1c0026e

scala> val vs = Reflection.findVals(x)
vs: Iterable[String] = List(y, xx)

scala> println(vs)
List(y, xx)

答案 1 :(得分:2)

使用SMirror

scala> implicit val mirror = scala.reflect.runtime.currentMirror
mirror: reflect.runtime.universe.Mirror = JavaMirror with scala.tool…

scala> import net.fwbrasil.smirror._
import net.fwbrasil.smirror._

scala> class Base {
  val x = 10
}   
defined class Base

scala> class Child extends Base {
  val y = 20
}   
defined class Child

scala> val x = new Child
x: Child = Child@448593d0

scala> x.reflect.vals
res5: List[net.fwbrasil.smirror.SInstanceVal[Child]] = List(val x: scala.Int (bound to Child@448593d0), val y: scala.Int (bound to Child@448593d0))

scala> x.reflect.vals.head.get
res7: Any = 10

答案 2 :(得分:0)

所以,这非常不优雅,但似乎有效:

import scala.reflect.runtime.{universe => ru}

object Reflection {

  val mirror = ru.runtimeMirror(this.getClass.getClassLoader)

  val ObjectClass = classOf[java.lang.Object];

  def findVals(x: Any) : Iterable[String] = findVals( x.getClass, List.empty );

  def findVals(clz: Class[_], accum : Iterable[String]): Iterable[String] = {

    clz match {
      case  ObjectClass => accum;
      case _ => {
        val theType = mirror.classSymbol(clz).toType
        val newVals = theType.members.collect({case x if x.isTerm => x.asTerm}).filter(_.isVal).map(_.name.toString)
        findVals( clz.getSuperclass, accum ++ newVals )
      }
    }
  }

}

则...

scala> class Base {
     |   val x = 10
     |   var z = 20
     | }
defined class Base

scala> class Child extends Base {
     | val y = 20
     | var a = 9
     | }
defined class Child

scala> val x = new Child
x: Child = Child@3093266d

scala> val vs = Reflection.findVals(x)
vs: Iterable[String] = List("y ", "x ")

scala> println(vs)
List(y , x )

似乎至少在目前,Scala reflection looks at the Java field确定了val的存在,所以我猜你只需要爬上类层次结构......我猜它看起来存在一个setter来区分val和var。再一次,不是那么可爱,但功能齐全。