2016-07-03 18 views
5

Giả sử rằng tôi có một lớp cha Generic:Trong Scala Reflection, Làm thế nào để có được tham số kiểu generic của một lớp con cụ thể?

class GenericExample[T](
         a: String, 
         b: T 
         ) { 

    def fn(i: T): T = b 
} 

và một lớp con cụ thể:

case class Example(
        a: String, 
        b: Int 
       ) extends GenericExample[Int](a, b) 

Tôi muốn để có được các thông số loại chức năng "fn" bằng cách phản ánh scala, vì vậy tôi lựa chọn và bộ lọc thông qua các thành viên của nó:

import ScalaReflection.universe._ 

val baseType = typeTag[Example] 

val member = baseType 
    .tpe 
    .member(methodName: TermName) 
    .asTerm 
    .alternatives 
    .map(_.asMethod) 
    .head 

    val paramss = member.paramss 
    val actualTypess: List[List[Type]] = paramss.map { 
     params => 
     params.map { 
      param => 
      param.typeSignature 
     } 
    } 

tôi đã mong scala để cho tôi kết quả chính xác, đó là List(List(Int)), thay vào đó tôi chỉ g ot chung List(List(T))

crunching qua các tài liệu tôi thấy rằng typeSignature là thủ phạm:

* This method always returns signatures in the most generic way possible, even if the underlying symbol is obtained from an 
* instantiation of a generic type. 

Và nó cho thấy tôi sử dụng giải pháp thay thế:

def typeSignatureIn(site: Type): Type 

Tuy nhiên, kể từ khi lớp Ví dụ không dài hơn chung chung, không có cách nào tôi có thể nhận được trang web từ typeTag [Ví dụ], bất cứ ai có thể đề nghị tôi làm thế nào để có được typeOf [Int] cho chỉ typeTag [Ví dụ]? Hoặc không có cách nào để làm điều đó và tôi phải hoàn nguyên về sự phản chiếu của Java?

Cảm ơn rất nhiều sự giúp đỡ của bạn.

UPDATE: Sau một số thử nghiệm nhanh chóng tôi thấy rằng thậm chí MethodSymbol.returnType không hoạt động như mong đợi, đoạn code sau:

member.returnType 

cũng mang lại T, annd nó không thể được sửa chữa bởi asSeenFrom, như đoạn mã sau không làm thay đổi kết quả:

member.returnType.asSeenFrom(baseType.tpe, baseType.tpe.typeSymbol.asClass) 
+0

Thậm chí trước khi thực sự đọc câu hỏi của bạn. Trong suốt toàn bộ kinh nghiệm của tôi với scala, một trong những khái niệm cốt lõi của nó là ủy thác càng nhiều càng tốt cho trình biên dịch. Vì vậy, phản ánh không phải là một trong những điều bạn sẽ làm trong scala. Tuy nhiên, bạn có thể sử dụng Macro hoặc ngữ cảnh (Manifests và ClassManifests) – caeus

+0

là Manifest trong quá trình không được chấp nhận? Và ClassManifest được đổi tên thành ClassTag? Tôi hy vọng ít nhất tôi cũng có thể lấy được ClassTag bị xóa khỏi phương thức, nhưng vẫn không thể làm như vậy. – tribbloid

Trả lời

0

tôi gửi bài giải pháp của tôi: Tôi nghĩ rằng không có lựa chọn thay thế do thiết kế của Scala:

Sự khác biệt cốt lõi giữa các phương pháp trong phản xạ Scala & Phản xạ Java đang được thực hiện: Phương pháp Scala compri ses của nhiều cặp dấu ngoặc đơn, gọi phương thức với các đối số đầu tiên chỉ xây dựng một lớp ẩn danh có thể lấy nhiều cặp dấu ngoặc hơn, hoặc nếu không có khung nào còn lại, hãy xây dựng một lớp NullaryMethod (a.k.a. gọi theo tên) có thể được giải quyết để mang lại kết quả của phương pháp. Vì vậy, các loại phương pháp scala chỉ được giải quyết ở cấp độ này, khi phương pháp đã được chia thành Phương thức & Chữ ký NullaryMethod.

Kết quả là nó trở nên rõ ràng rằng các loại quả chỉ có thể được sử dụng đệ quy:

private def methodSignatureToParameter_ReturnTypes(tpe: Type): (List[List[Type]], Type) = { 
    tpe match { 
     case n: NullaryMethodType => 
     Nil -> n.resultType 
     case m: MethodType => 
     val paramTypes: List[Type] = m.params.map(_.typeSignatureIn(tpe)) 
     val downstream = methodSignatureToParameter_ReturnTypes(m.resultType) 
     downstream.copy(_1 = List(paramTypes) ++ methodSignatureToParameter_ReturnTypes(m.resultType)._1) 
     case _ => 
     Nil -> tpe 
    } 
    } 

    def getParameter_ReturnTypes(symbol: MethodSymbol, impl: Type) = { 

    val signature = symbol.typeSignatureIn(impl) 
    val result = methodSignatureToParameter_ReturnTypes(signature) 
    result 
    } 

đâu impl là lớp sở hữu phương pháp, và symbol là những gì bạn nhận được từ Type.member(s) bởi scala phản ánh

+0

Điều đó đang được nói, tôi vẫn hy vọng phương pháp phản chiếu Scala và Java có thể tương thích hơn, vì cả hai đều có lợi thế: phương thức java nhanh hơn nhiều, nhưng kiểm tra kiểu thời gian chạy trong JVM khá lỏng lẻo, không có sự hoàn hảo ở đây: - < – tribbloid

4

có hai appr oaches mà tôi có thể đề nghị:

1) Reveal kiểu chung chung từ lớp cơ sở:

import scala.reflect.runtime.universe._ 

class GenericExample[T: TypeTag](a: String, b: T) { 
    def fn(i: T) = "" + b + i 
} 

case class Example(a: String, b: Int) extends GenericExample[Int](a, b) {} 

val classType = typeOf[Example].typeSymbol.asClass 
val baseClassType = typeOf[GenericExample[_]].typeSymbol.asClass 
val baseType = internal.thisType(classType).baseType(baseClassType) 

baseType.typeArgs.head // returns reflect.runtime.universe.Type = scala.Int 

2) Thêm phương pháp ngầm mà trả về kiểu:

import scala.reflect.runtime.universe._ 

class GenericExample[T](a: String, b: T) { 
    def fn(i: T) = "" + b + i 
} 

case class Example(a: String, b: Int) extends GenericExample[Int](a, b) 

implicit class TypeDetector[T: TypeTag](related: GenericExample[T]) { 
    def getType(): Type = { 
    typeOf[T] 
    } 
} 

new Example("", 1).getType() // returns reflect.runtime.universe.Type = Int 
+0

Tôi sợ trong hầu hết các trường hợp của tôi GenericExample là không xác định và được chôn sâu trong một đồ thị thừa kế tùy ý, vì vậy nó không khả thi để viết một Detector đồng hành, nhưng tôi sẽ thử groking giải pháp đầu tiên, cảm ơn rất nhiều cho câu trả lời của bạn! – tribbloid

+0

Không hoạt động: Lỗi: (72, 20) không được tìm thấy: giá trị nội bộ val baseType = internal.thisType (classType) .baseType (baseClassType). Tôi đang sử dụng Scala 2.10.6 cho khả năng tương thích với một thư viện – tribbloid

+0

Tôi nghĩ rằng tôi có một đầu mối bây giờ, MethodSymbol từ lớp siêu chung luôn xóa các tham số kiểu, để phá vỡ nó, tôi phải sử dụng MethodType thay thế. – tribbloid

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