Scala Reflection — загрузка или поиск классов на основе признаков

Предоставляет ли API отражения scala (2.10) какие-либо более простые средства поиска загруженных классов и фильтрации списка для определенных классов, которые реализуют определенный признак? т.е.;

trait Widget {
  def turn(): Int
}

class Cog extends Widget {
  def turn() = {
    5
  }
}

class Sprocket extends Widget {
  def turn() = {
   10
  }
}

Я хочу найти в библиотеке классов все, что расширяет Widget, и создать экземпляры этих классов. Таким образом, я бы получил экземпляр Cog и Sprocket.

Я сделал подобное в Java, перебирая каталоги классов, формируя имена классов и используя Class.forName для загрузки объекта класса для последующей проверки. Мне просто интересно, предоставляет ли API отражения scala какой-либо более простой способ поиска. Все примеры, которые я видел до сих пор, всегда начинались с создания экземпляра известного класса, а не с поиска доступных классов.


person Doswell    schedule 05.07.2013    source источник


Ответы (1)


Для этого предназначен ServiceLoader.

Я думаю, что API отражения упрощает выбор того, что вам нужно (т. е. для фильтрации, но не для запроса загрузчика классов).

Если под вашей фразой «поиск загруженных классов» вы действительно имеете в виду классы, которые уже загружены, см. этот вопрос для получить их.

Вы можете представить себе библиотеку виджетов с инициализатором, который просто гарантирует загрузку всех известных ему классов виджетов. Тогда клиенту нужно знать только инициализатор.

Типовой тест тот же.

val need = typeOf[Whatsit[Cog]]
for (x <- (ServiceLoader load classOf[Whatsit[_]]).asScala) { 
  val im = currentMirror reflect x 
  if (im.symbol.toType weak_<:< need)
    Console println s"$x is what I need"
  else
    Console println s"$x is not what I need, I'm looking for a $need"
} 

Где вы ищете что-то с параметрами типа:

trait Whatsit[+A <: Widget] {
  def widget: A
}

class Engine extends Whatsit[Cog] {
  def widget = new Cog
}

class FlyWheel extends Whatsit[Sprocket] {
  def widget = new Sprocket
}

Образец:

widgets.Engine@f9da0cd is what I need
widgets.FlyWheel@4cfdbb9f is not what I need, I'm looking for a widgets.Whatsit[widgets.Cog]

Если прошло десять лет с тех пор, как вы использовали ServiceLoader, и кому не нужно освежать знания:

apm@mara:~/tmp$ ls -R META-INF
META-INF:
MANIFEST.MF  services

META-INF/services:
widgets.Whatsit  widgets.Widget
apm@mara:~/tmp$ cat META-INF/services/widgets.Widget
widgets.Cog
widgets.Sprocket
apm@mara:~/tmp$ cat META-INF/services/widgets.Whatsit 
widgets.Engine
widgets.FlyWheel

Вещи:

package widgets

trait Widget {
  def turn(): Int
  override def toString = s"Widget ${getClass.getSimpleName}"
}

class Cog extends Widget {
  def turn() = 5
}

class Sprocket extends Widget {
  def turn() = 10
}

trait Whatsit[+A <: Widget] {
  def widget: A
  override def toString = s"Whatsit ${getClass.getSimpleName} of $widget"
}

class Engine extends Whatsit[Cog] {
  def widget = new Cog
}

class FlyWheel extends Whatsit[Sprocket] {
  def widget = new Sprocket
}

Сравнение Scala и Java. Я собирался понять, сколько LOC нужно getGenericInterfaces, и найти то, что нужно в Scala, но потом положил конец этому упражнению.

package findwidgets

import reflect._
import reflect.runtime.universe._
import reflect.runtime.currentMirror
import scala.collection.JavaConverters._
import java.util.ServiceLoader

object Test extends App {
  import widgets.{ Widget, Whatsit, Cog }
  val ws = (ServiceLoader load classOf[Widget]).asScala
  for (w <- ws) {
    Console println s"Turn a ${w.getClass} by ${w.turn}"
  }
  val need = typeOf[Whatsit[Cog]]
  for (x <- (ServiceLoader load classOf[Whatsit[Cog]]).asScala) {
    val im = currentMirror reflect x
    if (im.symbol.toType weak_<:< need)
      Console println s"$x is what I need"
    else
      Console println s"$x is not what I need, I'm looking for a $need"
    // java says:
    if (classOf[Whatsit[Cog]] isAssignableFrom x.getClass)
      Console println s"Um, OK, I'll take the $x"
    else
      Console println s"${classOf[Whatsit[Cog]]} isn't ass'able from ${x.getClass}"
  }
}
person som-snytt    schedule 06.07.2013
comment
Разве для этого не требуется, чтобы программист явно объявил свой интерфейс сервисом и перечислил все его реализации в META-INF/services/...? - person ghik; 06.07.2013
comment
Да, так дело в том, что библиотека говорит вам, что она предлагает. Вместо того, чтобы пытаться сканировать известную вселенную в поисках виджетов, в известном месте есть ресурс, который вы можете запросить для данного загрузчика классов. Я вставил другие части FYI. - person som-snytt; 07.07.2013
comment
Отлично! Спасибо сом! Это именно то, что мне было нужно. - person Doswell; 08.07.2013
comment
@Doswell стоит добавить, что загружаемый вами сервис может просто предлагать API для отображения доступных виджетов. - person som-snytt; 08.07.2013
comment
для тех, кто использует sbt и не знает, куда поместить папку META-INF: stackoverflow.com/questions/32916024/ - person gdoubleod; 03.10.2015