Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala Reflection - Loading or finding classes based on trait

Does the scala reflection API (2.10) provide any easier means of searching the loaded classes and filtering the list to specific classes which implement a defined trait? ie;

trait Widget {
  def turn(): Int
}

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

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

I want to search the class library for anything that extends Widget and instantiate those classes. So I would end up with an instance of Cog and Sprocket.

I've done similar in Java iterating through the class directories, forming class names and using Class.forName to load a Class object to then check. I'm just wondering if the scala reflection API gives any easier way to search. All examples I've seen thus far have always started from a known class being instantiated, and not from searching over available classes.

like image 565
Doswell Avatar asked Jul 05 '13 14:07

Doswell


1 Answers

This is what ServiceLoader is for.

I think the reflection API does make it easier to sort out what you need (i.e., for filtering but not for querying the class loader).

If, by your phrase, "searching the loaded classes", you really mean classes that are already loaded, see this question for getting them.

You could imagine a widgets library with an initializer that just ensures that all the widget classes it knows about are loaded. Then the client only needs to know the initializer.

The type test is the same.

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

Where you're looking for something with type parameters:

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
}

Sample:

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

In case it's been ten years since you used ServiceLoader, and who doesn't need a refresher:

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

Stuff:

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
}

Comparing Scala and Java. I was going to get a sense of how many LOC to getGenericInterfaces and find what you want in Scala, but then I put an end to the exercise.

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}"
  }
}
like image 110
som-snytt Avatar answered Nov 15 '22 22:11

som-snytt