2013-04-01 33 views
2

我想要做的事情應該非常簡單。我認爲。我想用反射來確定一個case類構造函數的參數名稱和類型。以下是REPL交互。你可以跳到「paramss」被調用的那一端。我想檢查或模式匹配內部列表中的符號,以確定'名稱'確實是一個字符串,'年齡'是一個Int。如何通過Scala中的反射獲得ctor參數類型2.10.1

我該怎麼做?

scala> case class Person(name: String, age: Int) 
defined class Person 

scala> import scala.reflect.runtime.universe._ 
import scala.reflect.runtime.universe._ 

scala> typeOf[Person] 
res0: reflect.runtime.universe.Type = Person 

scala> typeTag[Person] 
res1: reflect.runtime.universe.TypeTag[Person] = TypeTag[Person] 

scala> res1.mirror 
res2: reflect.runtime.universe.Mirror = JavaMirror with [email protected]4 of type class scala.tools.nsc.interpreter.IMain$TranslatingClassLoader with classpath [(memory)] and parent being [email protected] of type class scala.tools.nsc.util.ScalaClassLoader$URLClassLoader with classpath [file:/Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/jre/lib/resources.jar,file:/Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/jre/lib/rt.jar,file:/Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/jre/lib/jsse.jar,file:/Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/jre/lib/jce.jar,file:/Library/Java/JavaVirtualMachines/jdk1.7.0_13.jdk/Contents/Home/j... 
scala> res1.tpe 
res3: reflect.runtime.universe.Type = Person 

scala> res3.typeSymbol.asClass 
res4: reflect.runtime.universe.ClassSymbol = class Person 

scala> res2.reflectClass(res4) 
res5: reflect.runtime.universe.ClassMirror = class mirror for Person (bound to null) 

scala> res3.declaration(reflect.runtime.universe.nme.CONSTRUCTOR) 
res6: reflect.runtime.universe.Symbol = constructor Person 

scala> res6.asMethod 
res7: reflect.runtime.universe.MethodSymbol = constructor Person 

scala> res7.paramss 
res8: List[List[reflect.runtime.universe.Symbol]] = List(List(value name, value age)) 

回答

0

有趣的你應該問問!這正是我現在正在做的。

所以,首先,你有paramss。所以,你必須處理如何/是否支持多個參數列表。這裏有一些正是一個參數列表上堅持代碼,並捕獲:

val ctorParams : List[Symbol] = ctorParamss match { 
    case Nil => { 
println("Warning: Trying to populate a no-arg constructor from a Map. No data will be used."); 
Nil; 
    } 
    case head :: Nil => head; 
    case _ => throw new UnsupportedOperationException("Cannot populate a constructor with multiple param lists from a Map."); 
} 

現在我們需要的名稱和類型名稱:

val ctorParamNames = ctorParams.map(_.name.toString.trim); 
val ctorParamTypeNames = ctorParams.map(_.typeSignature.baseClasses(0).fullName.toString.trim); 

我仍然在做這個,在這個非常時刻,但到目前爲止,這一切都在爲我工作。祝你好運!

+0

非常感謝。困惑了一陣子。您是否發現除鏈接到ScalaDoc以外的任何文檔? –

+0

有這個斯卡拉反射指南:http://docs.scala-lang.org/overviews/reflection/overview.html我發現這篇相同的指南很有幫助:http://docs.scala-lang .org/overviews/reflection/symbols-trees-types.html但總而言之,所有使用反射和宏的東西都是反覆試驗(偶爾還有一個stackoverflow問題!)。請注意,運行時(而不是宏)反射是非常不重要的線程安全:http://docs.scala-lang.org/overviews/reflection/thread-safety.html我在隨機奇異性上浪費了很多時間'直到我明白了。 –

1

這就是你如何讓代表你的構造函數參數的類型Type對象:

val ctorParamss = typeOf[Person].member(nme.CONSTRUCTOR).asMethod.paramss 
val typeOfNameParam: Type = paramss(0)(0).typeSignature 

現在,你有對象,你可以在上面進行各種測試:

typeOfNameParam =:= typeOf[Int]  // is this type an Int ? 
typeOfNameParam <:< typeOf[Number] // is this type java.lang.Number or its subtype ? 
+0

我不認爲有任何方法可以通過反射將值轉換爲選定的類型嗎? IOW相當於:val a:Any = 2; val i = a.asInstanceOf [Int]可以通過反射來完成嗎? –

+0

@BillBarrington我不明白。類型轉換是一種編譯時操作,所以對於「通過反射進行投射」(除非你只是指類型檢查)並沒有意義。你需要什麼,你覺得它是怎樣的? – ghik

+0

是的,這更多與我對如何實現我最初設想的內容的困惑有關。我只想通過反射來檢查一個案例類,並使用Map中的值創建它的一個實例,其中映射鍵是ctor參數的名稱。我已經意識到我並不需要這樣做(爲了我的目的),但我不清楚你將如何通過反思來做到這一點。似乎你不得不以某種方式將實例的構造和它的(只讀)字段的構成分開?但是你是對的,我的問題在給定的背景下沒有意義。對於那個很抱歉。 –

1

使用SMirror

scala> import net.fwbrasil.smirror._ 
import net.fwbrasil.smirror._ 

scala> implicit val mirror = scala.reflect.runtime.currentMirror 
mirror: reflect.runtime.universe.Mirror = JavaMirror with scala.t... 

scala> case class Person(name: String, age: Int) 
defined class Person 

scala> val clazz = sClassOf[Person] 
clazz: net.fwbrasil.smirror.SClass[Person] = Person 

scala> val ctor = clazz.constructors.head 
ctor: net.fwbrasil.smirror.SConstructor[Person] = <init>(name: java.lang.String, age: scala.Int): Person 

scala> ctor.parameters 
res0: List[ctor.SParameterType] = List(name: java.lang.String, age: scala.Int) 

scala> ctor.parameters.head.sClass == sClassOf[String] 
res2: Boolean = true 
相關問題