Scala Reflection まとめ
導入
Scala2.10からreflectionの機能が拡張された
記事からの引用
他の JVM言語同様に,Scala の型はコンパイル時に消去 (erase) される.
これは,何らかのインスタンスのランタイム型をインスペクトしてもコンパイル時に Scala コンパイラが持つ型情報を全ては入手できない可能性があることを意味する.
型タグ (TypeTag) は,コンパイル時に入手可能な全ての型情報を実行時に持ち込むためのオブジェクトだと考えることができる. しかし,型タグは常にコンパイラによって生成されなくてはいけないことに注意してほしい.
この生成は暗黙のパラメータか context bound によって型タグが必要とされた時にトリガーされる. そのため,通常は,型タグは暗黙のパラメータか context bound によってのみ取得できる.
下記コードは,2.11で対応.2.11では,2.10で長ったらしいメソッド名や意味の通じにくいメソッド名を変更している.
実行時リフレクション
import scala.reflect.runtime.{universe => ru }
を使ったreflectionを見ていく
TypeTagを取る
type parameterを持つクラスでtypeを具象化したい時に使える
- class/method内でTypeTageでcontext bound
- ru.TypeTag[T]への参照をとっておく
- Typeを取得
RuntimeRelfectionApp.scala
package edu.kzk.reflection object RuntimeRelfectionApp extends App { // Import runtime universe import scala.reflect.runtime.{ universe => ru } val l = List(1, 2, 3) // Context bound for ru.TypeTag def getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T] // Get type info val theType = getTypeTag(l).tpe // Inspection for declarations and members theType.decls theType.members // Get constructor method symbol println(theType.decl(ru.termNames.CONSTRUCTOR)) }
Instanceの作成
- RuntimeMirrorを取得
- ClassSymbolを取得
- RuntimeMirrorからClassMirrorを取得
- MethodSymbol (Constructor相当)を取得
- ClassMirrorからMethodMirrorを取得
- MethodMirrorでコンストラクタ呼び出し
RuntimeRelfectionApp.scala
package edu.kzk.reflection object RuntimeRelfectionApp extends App { case class Person(name: String) // Reification of runtime type case class Person(name: String) // Get mirror var m = ru.runtimeMirror(getClass().getClassLoader()) // Get Person class symbol val classPerson = ru.typeOf[Person].typeSymbol.asClass //val classPerson = m.staticClass("edu.kzk.reflection.RuntimeRelfectionApp.Person") // Get class mirror val cm = m.reflectClass(classPerson) // Get constructor method symbol val ctor = ru.typeOf[Person].decl(ru.termNames.CONSTRUCTOR).asMethod // Get constructor val ctorm = cm.reflectConstructor(ctor); // Call constructor val person = ctorm("Mike") println(person) println() }
これを見ていると,applyを定義しないときしかreflectionできないのか?
Instance memberへのアクセス
- RuntimeMirrorを取得
- TermSymbolを取得
- InstanceMirrorを取得
- InstanceMirrorからFieldMirrorを取得
- FieldMirrorでget/set
RuntimeRelfectionApp.scala
package edu.kzk.reflection object RuntimeRelfectionApp extends App { case class Purchase(name: String, orderNumber: Int, var shipped: Boolean) val purchase = Purchase("Jeff Lebowski", 23819, false); // Get mirror m = ru.runtimeMirror(getClass().getClassLoader()) // Get term symbol (have to newTermName) val shippingTermSymb = ru.typeOf[Purchase].decl(ru.TermName("shipped")).asTerm // Get instance mirror val im = m.reflect(purchase) // Get field mirror val shippingFieldMirror = im.reflectField(shippingTermSymb); // Get field println(shippingFieldMirror.get) // Get field shippingFieldMirror.set(true) println(shippingFieldMirror.get) /* * Reification using classTag */ import scala.reflect._ val ct = classTag[String] val x = ct.runtimeClass.newInstance() + "This is relfected instance"; println(x); }
型タグとマニフェスト
TypeTagを使うことでコンパイル時入手可のうな全ての情報を実行時に持ち込める
scala.reflect.TypeTagであり,runtime.universe.TypeTagのではないので注意.
3つのTypeTagがある
- TypeTag
- Scala型の完全な型記述子
- ClassTag[List[String]]はList[String]を保持保持
- ClassTag
- Scala型の部分的な型記述子
- 消去された型情報のみを保持する
- ClassTag[List[String]]はListのみ保持
- WeakTypeTag
- 抽象型の型記述子
RuntimeRelfectionApp.scala
package edu.kzk.reflection object RuntimeRelfectionApp extends App { import scala.reflect._ val ct = classTag[String] val x = ct.runtimeClass.newInstance() + "This is relfected instance"; println(x); }