2013-07-05 10 views
7

Czy API scala reflection (2.10) zapewnia łatwiejszy sposób wyszukiwania załadowanych klas i filtrowania listy do konkretnych klas, które implementują zdefiniowaną cechę? ie;Odbicie Scala - Ładowanie lub wyszukiwanie klas w oparciu o cechę

trait Widget { 
    def turn(): Int 
} 

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

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

Chcę przeszukać bibliotekę klas dla wszystkiego, co rozszerza widget i tworzyć instancje tych klas. Więc skończyłbym z instancją Cog i Sprocket.

Zrobiłem podobne w iteracji Java poprzez katalogi klas, tworząc nazwy klas i używając Class.forName, aby załadować obiekt klasy, aby następnie sprawdzić. Zastanawiam się tylko, czy API scala reflection daje łatwiejszy sposób wyszukiwania. Wszystkie przykłady, które do tej pory widziałem, zawsze zaczynały się od utworzenia instancji znanej klasy, a nie od przeszukiwania dostępnych klas.

Odpowiedz

13

Do tego służy .

Myślę, że interfejs API do odczytywania ułatwia sortowanie potrzebnych informacji (np. Do filtrowania, ale nie do wysyłania zapytań do modułu ładującego klasy).

Jeśli przez wyrażenie "przeszukiwanie załadowanych klas", naprawdę masz na myśli klasy, które są już załadowane, zobacz this question, aby je uzyskać.

Można sobie wyobrazić bibliotekę widgetów z inicjatorem, który zapewnia, że ​​wszystkie klasy widżetów, o których wie, są załadowane. Wtedy klient musi tylko znać inicjator.

Test typu jest taki sam.

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

Jeżeli szukasz czegoś z parametrami Typ:

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 
} 

Próbka:

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

W przypadku minęło dziesięć lat, odkąd został użyty ServiceLoader, a kto nie potrzebujesz odświeżenia:

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

Porównując Scala i Java. Zamierzałem dowiedzieć się, ile LOC do getGenericInterfaces i znaleźć to, co chcesz w Scali, ale potem położyłem kres ćwiczeniu.

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

Czy to nie wymaga, że ​​programista jawnie deklaruje swój interfejs jako usługę i wymienia wszystkie jego implementacje w "META-INF/services/..."? – ghik

+0

Tak, więc chodzi o to, że biblioteka mówi ci, co oferuje. Zamiast próbować skanować znany wszechświat pod kątem widżetów, istnieje zasób w znanej lokalizacji, który można zapytać o dany moduł ładujący klasy. Wkleiłem inne kawałki do FYI. –

+0

Doskonale! Dzięki som! Dokładnie tego potrzebowałem. – Doswell

Powiązane problemy