Как использовать API scala отражения для получения всех содержащихся классов

Я получил такой класс:

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

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

class MyClass {

    object objA extends ThirdParty

    object objB extends WeatherIcon

}

Как я могу использовать API-интерфейс Scala для итерации через содержащиеся объекты и вызывать метод, если он является экземпляром класса ThirdParty?

Ответ 1

Основываясь на том, что написал soc, я получил следующее:

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
})

Позвольте мне объяснить совпадение шаблонов. Тип a val или object можно сравнивать напрямую, но функции имеют несколько иной тип. Здесь я сопоставляю методы, не содержащие списков параметров, и методы с нулевым параметром.

Здесь есть несколько отличий от социального ответа. Во-первых, я использую members вместо declarations. Это возвращает унаследованные элементы, а также те, которые объявлены самим MyClass.

Во-вторых, я проверяю, что это элемент значения, а не член типа. Вы можете использовать методы только для значений, поэтому он выглядел разумным ограничением, хотя, возможно, и ненужным. UPD. Метод isValue больше не доступен в 2.10.0-RC1, поэтому я удалил чек.

Наконец, я использую <:< вместо проверки каждого родителя на равенство.

Теперь, к вызову. Я собираюсь изменить код выше, так как вызов зависит от того, какой у вас член, поэтому нам лучше всего делать фильтрацию и вызов одновременно. Я собираюсь изменить с members на nonPrivateMembers, считая, что это нужно. UPD. nonPrivateMembers больше не доступен в 2.10.0-RC1, при необходимости используйте filter(!_.isPrivate).

И я также избегу использовать typeOf, который не будет работать с зеркалами REPL. UPD. В 2.10.0-RC1 typeOf работает тонко, но я сохраню скелет реализации без изменений.

Все вышесказанное в основном касается структуры вещей: что представляют собой члены типа, какие они есть, и так далее. Когда вы хотите использовать этот материал, вам нужны зеркала.

Всякий раз, когда у вас есть символ или тип для чего-то - класс, метод, obj и т.д., вы воздействуете на эту вещь через зеркало. Чтобы действовать (отражательно) на экземпляр объекта, вам нужно зеркало экземпляра. Чтобы действовать по методу, вам нужно зеркало метода и т.д.

Итак, попробуйте создать functon, чтобы выполнить запрошенное:

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
}

Обратите внимание, что вложенные модули не могут быть восстановлены с помощью Scala 2.10.0-M4 - это должно быть возможно с M5 или RC1. Чтобы проверить этот код на M4, замените код модуля на null.

Здесь пример:

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)

Ответ 2

Я не могу предложить полное решение, но, возможно, это начало:

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!

Я думаю, что есть намного более прямой путь, чем это.