Initial Values を利用して Nothing 型の値を作ることは可能か

どうでもいい話な上に長いので、暇な人だけどうぞ。


java では、全ての型に対して "default value" というのが仕様で定義されています。

For type byte, the default value is zero, that is, the value of (byte)0.
For type short, the default value is zero, that is, the value of (short)0.
For type int, the default value is zero, that is, 0.
For type long, the default value is zero, that is, 0L.
For type float, the default value is positive zero, that is, 0.0f.
For type double, the default value is positive zero, that is, 0.0d.
For type char, the default value is the null character, that is, '\u0000'.
For type boolean, the default value is false.
For all reference types (§4.3), the default value is null.

scala にも、これ相当の値で初期化するための構文が用意されています。4.2 Variable Declarations and Definitions より。

A variable definition var x: T = _ can appear only as a member of a template. It
introduces a mutable field with type T and a default initial value. The default value
depends on the type T as follows:
0 if T is Int or one of its subrange types,
0L if T is Long,
0.0f if T is Float,
0.0d if T is Double,
false if T is Boolean,
() if T is Unit,
null for all other types T.

そういえば、随分前にそんな話があったなあ…忘れてました。

ところで Nothing 型の値は、通常作ることができません。値の王 null でもってしても不可能です。

scala> val x : Nothing = null
<console>:7: error: type mismatch;
 found   : Null(null)
 required: Nothing
       val x : Nothing = null

Nothing は全ての型のスーパータイプなので Null では型があいません。

しかし "_" による初期化を使えば、仕様の穴をついて Nothing 型の値を作れそうな気がしますね…!やってみましょう!

scala> var x : Nothing = _
java.lang.NullPointerException
        at .<init>(<console>:14)
        at .<clinit>(<console>)
        at $print(<console>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        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(Unknown Source)

実装に阻まれました…残念賞。

class ファイルを java に逆コンパイルすれば分かるのですが、"_" を利用した初期化は、結局のところ java の未初期化変数と同じ扱いになるんですね。sun.reflect.NativeMethodAccessorImpl.invoke0 とかがどうやらそれっぽく見えます。とすると、プリミティブでない scala.Int や scala.Unit も駄目そうな気がしますが、なんだかよく分からないけれど大丈夫らしいです。不思議ですね。boxing 周りや Unit 周りの scala の内部実装的な話に詳しくないので、そのうち調べようかなあ…どうでもいいなあ…どうでもいいですね…

おしまい。