CLOVER🍀

That was when it all began.

ScalaのReflectionについて、まとめてみる - オマケ

最後、ちょっと蛇足的に。

これまでに書いたエントリのまとめは、こちらです。

導入編
http://d.hatena.ne.jp/Kazuhira/20130730/1375192075
定義情報取得編 - 1
http://d.hatena.ne.jp/Kazuhira/20130801/1375370390
定義情報取得編 - 2
http://d.hatena.ne.jp/Kazuhira/20130803/1375526971
インスタンス操作編
http://d.hatena.ne.jp/Kazuhira/20130804/1375604912
オマケ
http://d.hatena.ne.jp/Kazuhira/20130804/1375607954

現在(Scala 2.10.2)のScalaのReflectionで、注意しておいた方がいいこと、気になったことを少し。

スレッドセーフ?

現時点のScalaのReflectionは、スレッドセーフではありません。

Thread Safety
http://docs.scala-lang.org/overviews/reflection/thread-safety.html

日本語訳
http://docs.scala-lang.org/ja/overviews/reflection/thread-safety.htm

どうも、リフレクションまわりの初期化がスレッドセーフではないようです。UniverseやType、Symbolなど…。

これが解決されるまでは、ちょっと実戦投入はしづらい感じですね。マクロで使う分には、明示的にスレッドを使わなければきっと大丈夫だと。

この問題は、SI-6240で報告されていて、まだ未解決です。

SI-6240
https://issues.scala-lang.org/browse/SI-6240

Scala 2.10.3では直るのでしょうか?どちらにしても、その時にあんまりAPI変わらないといいなぁ〜。現時点で実験的導入とはいえ。

valを書き換えられる?

これは、Mirrorを使っている時に気付いたのですが、valに対してFieldMirror#setでvalの値を書き換えることができます。

以下、検証コード。

対象のクラス。

class SampleClass {
  val field: String = "val field"
}

valを書き換えるコード。

val instance = new SampleClass
require(instance.field == "val field")

val runtimeMirror = universe.typeTag[SampleClass].mirror
val theType = universe.typeOf[SampleClass]

val instanceMirror = runtimeMirror.reflect(instance)
val valField = theType.member(universe.newTermName("field")).asMethod
val valFieldMirror = instanceMirror.reflectField(valField)

valFieldMirror.set("val broken")

require(instance.field == "val broken")

何事もなく変わりましたが、こういうもん?

自分はこれが嫌だったので、前のMirrorのまとめではvalに対しては面倒でもgetterを使い

val valField = theType.member(universe.newTermName("valField")).asMethod.getter.asMethod

varに対してはgetterとsetterを使いました。

val varField = theType.member(universe.newTermName("varField")).asMethod
val varGetField = varField.getter.asMethod  // varのgetter
val varSetField = varField.setter.asMethod  // varのsetter

valに対するMethodSymbol#getterは、NoSymbolとなりますので。

とりあえず、スレッドセーフ性については直ってくれないかなぁ、と…。