Skip to content

Instantly share code, notes, and snippets.

@hogehiko
Last active August 29, 2015 14:20
Show Gist options
  • Save hogehiko/590fb1c828e47137817f to your computer and use it in GitHub Desktop.
Save hogehiko/590fb1c828e47137817f to your computer and use it in GitHub Desktop.
Scala Reflection メモ(フィールド)
/**
Scalaで実行時リフレクションを使う機会があったのでメモしておく。
なお、下記の内容はScala 2.11.xによる。
今回は実行時リフレクションによってとあるオブジェクトの変数の内容を変更する。
特にJavaとの違いでポイントと感じるのはジェネリック型の扱いである。
*/
import scala.reflect.runtime.{universe => ru}
/*
Javaのジェネリクスにおいて、コンパイル後のバイトコードからは型の情報が消えている。
したがって、型パラメータをもつクラスのオブジェクトでは、パラメタとして与えられたクラスのインスタンスは生成できない。
もっとも、パラメタ型のオブジェクトが外部から与えられればそこからクラスオブジェクトを取得してリフレクションライブラリを利用可能だ)
Scala 2.11以降に導入されたリフレクションライブラリには、TypeTagと呼ばれる、型消去モデルを補う概念が導入されている。
この型タグの取得の仕方が少々ややこしいのだが、まず単純に型タグを生成してみよう
(なお、型名をソースコードに書けるならTypeを直接利用すればいいので下記のコードは実用上の意味がない)。
val t = ru.typeTag[Int]
このru.typeTagメソッドがimplicit引数をいくつか受け取るのだが、コンテキストへのロードをプログラマが行うのではなく、
コンパイラが暗黙的に行うようだ(そのようにしか出来ない仕様らしい)。
*/
object Reflect extends App{
/*
任意の型の型タグを取り出すには以下のようにする。
*/
def getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T]
/*
TypeTag[任意の型]のオブジェクトをimplicit引数として受けるとメソッドを呼ぶと、コンパイラが暗黙的に該当の値を割り当ててくれるということだろうか。
これで任意の型のTypeTagが取得できるようになった。
今回は特定のフィールドの変更を行うため、対応するシンボルを取得する。
変更したいフィールドの名称をhogeとする。
まずTermNameを作り、
*/
val tn = ru.TermName("hoge")
/*
取得したTypeTagからTypeを取り出し、TermNameに該当するTermSymbolを取得する。
*/
val tag = getTypeTag(h)
val ts = tag.tpe.decl(tn).asTerm
/*
TypeTagが取得できるようになればあとはそれほど難しくない。
ではオブジェクトへの操作を行ってみよう。
*/
class Hoge(var hoge:Int)
val h = new Hoge(0)
/*
リフレクションを使って実際に作用を起こすには、mirrorと呼ばれるAPI群を使う。
鏡を使って自分自身に操作を反射するということだろうか。
はじめにランタイムミラーを取得する。
*/
val rm = ru.runtimeMirror(getClass.getClassLoader)
/*
次にインスタンスミラーを取得
*/
val im = rm.reflect(h)
/*
そしてフィールドのミラーを取得する。ここで、前半で作成したTermSymbolを使用する。
このミラーにはフィールドを操作するためのsetやgetなどが含まれている。
*/
val fm = im.reflectField(ts)
fm.set(100)
/*
これでhogeフィールドに100がセットできた。
*/
println(h.hoge) // -> "100"を印字
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment