2013-07-05 13 views
7

Bietet die scala reflection API (2.10) eine einfachere Möglichkeit, die geladenen Klassen zu durchsuchen und die Liste nach bestimmten Klassen zu filtern, die ein definiertes Merkmal implementieren? dh;Scala Reflection - Laden oder Suchen von Klassen basierend auf Merkmal

Ich möchte die Klassenbibliothek nach allem durchsuchen, das Widget erweitert und diese Klassen instanziiert. Also würde ich mit einer Instanz von Cog und Sprocket enden.

Ich habe in Java ähnlich durch die Klassenverzeichnisse iteriert, Klassennamen gebildet und mit Class.forName ein Klassenobjekt zum Überprüfen geladen. Ich frage mich nur, ob die Scala-Reflection-API eine einfachere Suche ermöglicht. Alle Beispiele, die ich bisher gesehen habe, haben immer damit begonnen, dass eine bekannte Klasse instanziiert wird, und nicht, um verfügbare Klassen zu durchsuchen.

Antwort

13

Dies ist, was ServiceLoader ist für.

Ich denke, dass die Reflection-API macht es leichter zu sortieren, was Sie brauchen (d. H. Zum Filtern, aber nicht zum Abfragen der Klassenlader).

Wenn Sie mit dem Ausdruck "die geladenen Klassen durchsuchen" wirklich Klassen meinen, die bereits geladen sind, lesen Sie this question, um sie zu erhalten.

Sie können sich eine Widgets-Bibliothek mit einem Initialisierer vorstellen, der nur dafür sorgt, dass alle Widget-Klassen, die er kennt, geladen werden. Dann muss der Client nur den Initialisierer kennen.

Der Typprüfung ist der gleiche.

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

Wo Sie etwas mit Typ-Parameter freuen:

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 
} 

Probe:

[email protected] is what I need 
[email protected] is not what I need, I'm looking for a widgets.Whatsit[widgets.Cog] 

Falls es 10 Jahre her, seit Sie ServiceLoader, verwendet und wer nicht brauchen einen Auffrischer:

[email protected]:~/tmp$ ls -R META-INF 
META-INF: 
MANIFEST.MF services 

META-INF/services: 
widgets.Whatsit widgets.Widget 
[email protected]:~/tmp$ cat META-INF/services/widgets.Widget 
widgets.Cog 
widgets.Sprocket 
[email protected]:~/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 
} 

Vergleich Scala und Java. Ich wollte herausfinden, wie viele LOC zu getGenericInterfaces und finden Sie, was Sie in Scala wollen, aber dann habe ich ein Ende der Übung.

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

Muss dies nicht bedeuten, dass der Programmierer seine Schnittstelle explizit als Dienst deklariert und alle seine Implementierungen in 'META-INF/services/...' auflistet? – ghik

+0

Ja, also der Punkt ist, dass die Bibliothek Ihnen sagt, was es bietet. Anstatt zu versuchen, das bekannte Universum nach Widgets zu durchsuchen, gibt es an einem bekannten Speicherort eine Ressource, die Sie für einen bestimmten Klassenlader abfragen können. Ich habe die anderen Stücke eingefügt. –

+0

Ausgezeichnet! Danke, som! Genau das habe ich gebraucht. – Doswell