Scala Reflection - Loading or finding classes based on trait

后端 未结 1 341
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-18 01:36

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

相关标签:
1条回答
  • 2020-12-18 02:11

    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}"
      }
    }
    
    0 讨论(0)
提交回复
热议问题