scala -Xcheckinit
だと TransietnVariablesトレイトで定義したフィールドへのアクセス時に以下のような例外が発生する。
scala.UninitializedFieldError: Uninitialized field: <console>: 20
at Container._boolean(<console>:20)
at TransientVariables$class.boolean(<console>:31)
at Container.boolean(<console>:20)
at .<init>(<console>:39)
at .<clinit>(<console>)
at .<init>(<console>:11)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.scala:920)
at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)
at scala.tools.nsc.io.package$$anon$2.run(package.scala:25)
at java.lang.Thread.run(Thread.java:680)
-Xcheckinit
なしの場合、例外は発生しない。
また、フィールド定義を _
にすれば UninitializedFieldError にはならない。
例えば、
@transiet var _string: String = _
と定義した場合、例外は発生せずstring: null
が出力される。
そもそも UninitializedFieldError ってどういう意味?
一言でいうと、-Xcheckinit
が指定されているとき、コンストラクタによる初期化前にフィールドにアクセスすると発生する。
参考: Initialization Order · paulp/scala-faq Wiki
内部的にはフィールドが初期化済みかどうかをフラグで管理していて、フラグが立っていないのにフィールドにアクセスすると例外を投げるようにしているらしい。
今回はどうかというと、ObjectInputStreamが以下のような動きをしていると想像している。
- Objectのデフォルトコンストラクタを呼び出してインスタンス生成
- このとき、内部ではTransientVariablesで定義されたフィールドが初期化される。しかし、まだ「フィールドをinitializeした」というフラグが立てられない。
- デシリアライズした非transientなフィールドをセット
その結果、
全てのフィールドに値がセットされているが、@transientなフィールドについて「フィールドをinitializeした」フラグが立っていない
という状態になっている。
この状態でフィールドにアクセスしているため、例外が発生しているのだろう。
しかし、
@transiet var _string: String = _
のように定義した場合に例外が投げられない理由は未調査。このように定義するとフラグ管理から除外されるのか?
-Xcheckinit 外せばよくない?
Play 2.0 では -Xcheckinit
がデフォルトでついている。どういう意図でつけているのか不明なので、どうしたものやら・・。