2012-06-15 30 views
11

tôi đã loại lớp:Làm thế nào để sử dụng scala phản ánh API để có được tất cả các lớp chứa

trait ThirdParty { def invoke = println("right") } 

trait WeatherIcon { def invoke = println("wrong") } 

class MyClass { 

    object objA extends ThirdParty 

    object objB extends WeatherIcon 

} 

Làm thế nào tôi có thể sử dụng sự phản xạ API Scala để lặp qua các đối tượng chứa và gọi một phương pháp nếu nó là một thể hiện của lớp học ThirdParty?

Trả lời

14

Dựa trên những gì soc viết, tôi nhận điều này:

import scala.reflect.runtime.universe._ 
val members = typeOf[MyClass].members.filter(_.typeSignature match { 
    case tpe if tpe <:< typeOf[ThirdParty] => true 
    case NullaryMethodType(tpe) if tpe <:< typeOf[ThirdParty] => true 
    case MethodType(Nil, tpe) if tpe <:< typeOf[ThirdParty] => true 
    case _ => false 
}) 

Hãy để tôi giải thích các mô hình phù hợp. Loại val hoặc object có thể được so sánh trực tiếp, nhưng chức năng có loại hơi khác. Ở đây tôi đang đối chiếu với các phương thức không có danh sách tham số và các phương thức có danh sách tham số zero-arity.

Có một số khác biệt ở đây so với câu trả lời của soc. Trước tiên, tôi sử dụng members thay vì declarations. Điều đó trả về các thành viên được kế thừa cũng như các thành viên được khai báo trên chính mình là MyClass.

Thứ hai, tôi kiểm tra xem đó có phải là thành viên giá trị không, thay vì thành viên loại. Bạn chỉ có thể gọi các phương thức trên các giá trị, vì vậy nó có vẻ là một hạn chế hợp lý, mặc dù có thể không cần thiết. cập nhật. Phương thức isValue không còn có sẵn trong 2.10.0-RC1, vì vậy tôi đã xóa séc.

Cuối cùng, tôi sử dụng <:< thay vì kiểm tra từng phụ huynh để bình đẳng.

Bây giờ, đến lời gọi. Tôi sẽ thay đổi mã trên kể từ khi yêu cầu phụ thuộc vào loại thành viên bạn có, vì vậy chúng tôi tốt nhất nên lọc và gọi cùng một lúc. Tôi cũng sẽ thay đổi từ số members thành nonPrivateMembers, giả sử đó là điều mong muốn. cập nhật. nonPrivateMembers không còn có sẵn trong 2.10.0-RC1, sử dụng filter(!_.isPrivate) nếu cần.

Và tôi cũng sẽ tránh sử dụng typeOf, sẽ không hoạt động với các gương trên REPL. cập nhật. Trong 2,10.0-RC1 typeOf đang hoạt động tốt, nhưng tôi sẽ giữ cho bộ xương của việc triển khai không thay đổi.

Tất cả những điều trên cơ bản liên quan đến cấu trúc của sự vật: các thành viên thuộc loại nào, loại thành viên nào, v.v. Khi bạn muốn hãy sử dụng công cụ này, bạn cần gương.

Bất cứ khi nào bạn có biểu tượng hoặc loại cho thứ gì đó - một lớp học, phương pháp, obj, v.v. - bạn hành động trên điều đó thông qua một gương . Để hành động (phản ánh) trên một thể hiện của một đối tượng, bạn cần một cá thể gương. Để hành động trên một phương pháp, bạn cần một phương pháp nhân bản, và như vậy.

Vì vậy, hãy cố gắng xây dựng một functon để làm những gì được yêu cầu:

import scala.reflect.runtime.universe._ 
def invoke[Target : TypeTag](obj: Any): Seq[Target] = { 
    val mirror = runtimeMirror(obj.getClass.getClassLoader) 
    val insMirror = mirror reflect obj 
    val originType = insMirror.symbol.typeSignature 
    val targetType = typeTag[Target].tpe 

    val members = originType.members 

    val result = members collect (member => member.typeSignature match { 
    case tpe if tpe <:< typeOf[ThirdParty] => 
     if (member.isModule) 
     (insMirror reflectModule member.asModule).instance 
     else 
     (insMirror reflectField member.asTerm).get 
    case NullaryMethodType(tpe) if tpe <:< typeOf[ThirdParty] => 
     (insMirror reflectMethod member.asMethod).apply() 
    case MethodType(Nil, tpe) if tpe <:< typeOf[ThirdParty] => 
     (insMirror reflectMethod member.asMethod).apply() 
    }) 

    result.map(_.asInstanceOf[Target]).toSeq 
} 

Lưu ý rằng lồng module không thể được phục hồi với Scala 2.10.0-M4 - mà ta có thể với M5 hoặc RC1. Để kiểm tra mã này bằng M4, hãy thay thế mã mô-đun bằng null.

Dưới đây là một ví dụ:

scala> class MyClass { 
    object objA extends ThirdParty 
    object objB extends WeatherIcon 
    val aVal = new ThirdParty {} 
    val bVal = new WeatherIcon {} 
    def aDef = new ThirdParty {} 
    def bDef = new WeatherIcon {} 
    def anotherDef() = new ThirdParty {} 
    def yetAnotherDef() = new WeatherIcon {} 
    } 
defined class MyClass 


scala> invoke[ThirdParty](new MyClass) 
res88: Seq[ThirdParty] = List([email protected], [email protected], [email protected], null) 
+0

Phần lớn đẹp hơn ví dụ của tôi! Cảm ơn! – soc

+0

Tôi nghĩ typeSignature trả về NullaryMethodTypes, không phải là các kiểu con của ThirdParty? Bạn có thể in '_.typeSignature.kind' không? –

+1

Phần yêu cầu có thể được thực hiện bằng API gương. Thông tin thêm tại đây: http://stackoverflow.com/questions/11020746/get-companion-object-instance-with-new-scala-reflection-api/11031443#11031443. Trong ngắn hạn, bạn sẽ cần phải thực hiện 'runtimeMirror (getClass.getClassLoader) .reflect (). ReflectMethod () ()' –

2

tôi không thể đưa ra một giải pháp hoàn chỉnh, nhưng có lẽ đây là một sự khởi đầu:

import reflect.runtime.universe._ 

val myClassType = typeOf[MyClass] // Get the type of McClass 
val thirdPartyType = typeOf[ThirdParty] // Get the type of ThirdParty 
val methodToInvoke = newTermName("invoke") 

val declsOfMyClass = myClassType.declarations // Get the declarations of MyClass 

val subtypesOfThirdParty = 
    declsOfMyClass.filter(_.typeSignature.parents.contains(thirdPartyType)) 

val methodsToInvoke =    // Now we have the methods. 
    subtypesOfThirdParty.map(tps => tps.typeSignature.member(methodToInvoke)) 

// TODO: Invoke! 

Tôi đoán có một cách thẳng về phía trước nhiều hơn này.

Các vấn đề liên quan