2012-06-15 10 views
11

Tengo este tipo de clase:Cómo utilizar la API Scala reflexión para llegar a todas las clases contenidas

trait ThirdParty { def invoke = println("right") } 

trait WeatherIcon { def invoke = println("wrong") } 

class MyClass { 

    object objA extends ThirdParty 

    object objB extends WeatherIcon 

} 

¿Cómo puedo usar la API de reflexión Scala para iterar a través de los objetos contenidos e invocar un método si se trata de una instancia de una clase ThirdParty?

Respuesta

14

Sobre la base de lo que escribió soc, tengo esto:

import scala.reflect.runtime.universe._ 
val members = typeOf[MyClass].members.filter(_.typeSignature match { 
    case tpe if tpe <:< typeOf[ThirdParty] => true 
    case NullaryMethodType(tpe) if tpe <:< typeOf[ThirdParty] => true 
    case MethodType(Nil, tpe) if tpe <:< typeOf[ThirdParty] => true 
    case _ => false 
}) 

Voy a explicar la coincidencia de patrón. El tipo de val o object se puede comparar directamente, pero las funciones tienen un tipo ligeramente diferente. Aquí estoy haciendo coincidir los métodos sin listas de parámetros y los métodos con una lista de parámetros de aria cero.

Aquí hay algunas diferencias en comparación con la respuesta de soc. Primero, uso members en lugar de declarations. Eso devuelve los miembros heredados, así como los que se declaran en MyClass sí mismo.

En segundo lugar, compruebo que es un miembro de valor, a diferencia de un miembro de tipo. Solo puede invocar métodos sobre valores, por lo que parecía una restricción razonable, aunque tal vez innecesaria. upd. El método isValue ya no está disponible en 2.10.0-RC1, por lo que eliminé el cheque.

Finalmente, uso <:< en lugar de verificar la igualdad de cada padre.

Ahora, a la invocación. Voy a cambiar el código anterior, ya que la invocación depende del tipo de miembro que tenga, por lo que será mejor que hagamos el filtrado y la invocación al mismo tiempo. También cambiaré de members a nonPrivateMembers, suponiendo que eso es lo que quiero. upd. nonPrivateMembers ya no está disponible en 2.10.0-RC1, use filter(!_.isPrivate) si es necesario.

Y también evitaré el uso de typeOf, que no funcionará con espejos en el REPL. upd. En 2.10.0-RC1 typeOf está funcionando bien, pero mantendré el esqueleto de la implementación sin cambios.

Todo lo anterior se refiere básicamente a la estructura de las cosas: qué son los miembros de un tipo, qué tipo de miembros son, y así sucesivamente. Cuando quiera use esto, necesita espejos.

Siempre que tenga un símbolo o un tipo para algo - una clase, método, obj, etc. - actúa sobre esa cosa a través de un espejo . Para actuar (reflexivamente) en una instancia de un objeto, necesita un espejo de instancia. Para actuar en un método, necesita un espejo de método, y así sucesivamente.

Así que vamos a tratar de construir una functon a hacer lo que se solicitó:

import scala.reflect.runtime.universe._ 
def invoke[Target : TypeTag](obj: Any): Seq[Target] = { 
    val mirror = runtimeMirror(obj.getClass.getClassLoader) 
    val insMirror = mirror reflect obj 
    val originType = insMirror.symbol.typeSignature 
    val targetType = typeTag[Target].tpe 

    val members = originType.members 

    val result = members collect (member => member.typeSignature match { 
    case tpe if tpe <:< typeOf[ThirdParty] => 
     if (member.isModule) 
     (insMirror reflectModule member.asModule).instance 
     else 
     (insMirror reflectField member.asTerm).get 
    case NullaryMethodType(tpe) if tpe <:< typeOf[ThirdParty] => 
     (insMirror reflectMethod member.asMethod).apply() 
    case MethodType(Nil, tpe) if tpe <:< typeOf[ThirdParty] => 
     (insMirror reflectMethod member.asMethod).apply() 
    }) 

    result.map(_.asInstanceOf[Target]).toSeq 
} 

Tenga en cuenta que anida módulos no se pueden recuperar con Scala 2.10.0-M4 - que debería ser posible con M5 o RC1. Para probar este código con M4, reemplace el código del módulo con null.

He aquí una muestra:

scala> class MyClass { 
    object objA extends ThirdParty 
    object objB extends WeatherIcon 
    val aVal = new ThirdParty {} 
    val bVal = new WeatherIcon {} 
    def aDef = new ThirdParty {} 
    def bDef = new WeatherIcon {} 
    def anotherDef() = new ThirdParty {} 
    def yetAnotherDef() = new WeatherIcon {} 
    } 
defined class MyClass 


scala> invoke[ThirdParty](new MyClass) 
res88: Seq[ThirdParty] = List([email protected], [email protected], [email protected], null) 
+0

Mucho mejor que mi ejemplo! ¡Gracias! – soc

+0

Creo que typeSignature devuelve NullaryMethodTypes, que no son subtipos de ThirdParty? ¿Podrías imprimir '_.typeSignature.kind'? –

+1

La parte de invocación se puede hacer con la API de réplicas. Más información aquí: http://stackoverflow.com/questions/11020746/get-companion-object-instance-with-new-scala-reflection-api/11031443#11031443. En resumen, tendrá que hacer 'runtimeMirror (getClass.getClassLoader) .reflect (). ReflectMethod () ()' –

2

que no puede ofrecer una solución completa, pero tal vez esto es un comienzo:

import reflect.runtime.universe._ 

val myClassType = typeOf[MyClass] // Get the type of McClass 
val thirdPartyType = typeOf[ThirdParty] // Get the type of ThirdParty 
val methodToInvoke = newTermName("invoke") 

val declsOfMyClass = myClassType.declarations // Get the declarations of MyClass 

val subtypesOfThirdParty = 
    declsOfMyClass.filter(_.typeSignature.parents.contains(thirdPartyType)) 

val methodsToInvoke =    // Now we have the methods. 
    subtypesOfThirdParty.map(tps => tps.typeSignature.member(methodToInvoke)) 

// TODO: Invoke! 

supongo que hay una manera mucho más recta hacia adelante que esto.

Cuestiones relacionadas