2013-07-05 34 views
7

API phản xạ scala (2.10) có cung cấp phương tiện dễ dàng hơn để tìm kiếm các lớp đã tải và lọc danh sách không? tức là;Scala Reflection - Tải hoặc tìm các lớp dựa trên đặc tính

trait Widget { 
    def turn(): Int 
} 

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

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

Tôi muốn tìm kiếm thư viện lớp cho mọi thứ mở rộng Widget và khởi tạo các lớp đó. Vì vậy, tôi sẽ kết thúc với một ví dụ của CogSprocket.

Tôi đã thực hiện tương tự trong Java lặp qua các thư mục lớp, tạo các tên lớp và sử dụng Class.forName để tải một đối tượng Lớp để kiểm tra. Tôi chỉ tự hỏi liệu API phản chiếu scala có cung cấp cách tìm kiếm dễ dàng hơn không. Tất cả các ví dụ mà tôi đã thấy từ trước đến nay luôn bắt đầu từ một lớp đã biết được khởi tạo, và không phải từ việc tìm kiếm trên các lớp có sẵn.

Trả lời

13

Đây là những gì ServiceLoader dành cho.

Tôi nghĩ rằng API phản chiếu giúp bạn dễ dàng phân loại những gì bạn cần (tức là, để lọc nhưng không phải để truy vấn trình tải lớp).

Nếu, theo cụm từ của bạn, "tìm kiếm các lớp đã tải", bạn thực sự có nghĩa là các lớp đã được tải, hãy xem this question để nhận chúng.

Bạn có thể tưởng tượng một thư viện tiện ích con bằng trình khởi tạo chỉ đảm bảo rằng tất cả các lớp tiện ích mà nó biết đều được tải. Sau đó, khách hàng chỉ cần biết trình khởi tạo.

Kiểm tra loại giống nhau.

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

Trong trường hợp bạn đang tìm kiếm một cái gì đó với các thông số type:

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 
} 

mẫu:

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

Trong trường hợp nó được mười năm kể từ khi bạn sử dụng ServiceLoader, và ai không cần bồi dưỡng:

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

So sánh Scala và Java. Tôi sẽ có được một cảm giác có bao nhiêu LOC để getGenericInterfaces và tìm thấy những gì bạn muốn trong Scala, nhưng sau đó tôi chấm dứt tập thể dục.

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

Điều này không yêu cầu người lập trình tuyên bố rõ ràng giao diện của mình dưới dạng dịch vụ và liệt kê tất cả các triển khai của nó trong 'META-INF/services/...'? – ghik

+0

Có, do đó, điểm là thư viện cho bạn biết những gì nó cung cấp. Thay vì cố gắng quét vũ trụ đã biết cho các widget, có một tài nguyên ở một vị trí đã biết mà bạn có thể truy vấn cho một trình nạp lớp đã cho. Tôi dán các phần khác FYI. –

+0

Tuyệt vời! Cảm ơn som! Đó chính xác là những gì tôi cần. – Doswell