読者です 読者をやめる 読者になる 読者になる

KZKY memo

自分用メモ.

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);
}