2013-04-03 2 views
1

Я использую Scala 2.10.1, и я пытаюсь определить метод, который будет извлекать все vals (включая унаследованные) из объекта.Не удается получить унаследованные vals с отражением Scala

У меня есть следующие:

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.

Может кто-нибудь сказать мне, в чем проблема? Я делаю что-то неправильно?

+0

Пожалуйста, смотрите http://stackoverflow.com/questions/15784320/enumerate-the-fields-of-a-subclass-in-scala/15785798#15785798 –

+0

Это действительно Безразлично Ответь на мой вопрос –

+0

О, извините. Я неправильно понял проблему. –

ответ

4

За Why don't Scala case class fields reflect as public? вы должны использовать isAccessor вместо isVal.

Я на самом деле с помощью isGetter и setter правильно фильтровать 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 = [email protected] 

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

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

Проблема в том, что это также вернет вамры. Например, если 'x' в' Base' становится var, оно также будет возвращено вместе с 'x_ $ eq'. –

+0

обновлено - немного взломать, но он работает. – sourcedelica

+0

сделал меньше взлома. – sourcedelica

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 = [email protected] 

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 от var. Опять же, не так прекрасно, но функционально.

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 = [email protected] 

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

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