定数式

どうもIBM系のJavaコンパイラと同じ動きしなくてはまっていた点。

  1. プリミティブ型のリテラルと文字列リテラルは定数式
  2. 定数式同士の比較は定数式
  3. 定数式同士の文字列連結演算(+)は定数式

定数式だと何が嬉しいのかというと、定数式の数値は代入変換の際にナローイング変換をかけてくれます*1

簡単に試してみると、下記のようなコードはコンパイル可能です。

final int a = 1;
char c = a;

int は charに代入可能ではありませんが、aはconstant variableになるため、その初期値である定数式1として評価されます。1はcharに代入可能なので、コンパイルエラーにはなりません。

さらに、文字列の連結および比較も定数式で、定数式の文字列はString.intern()が適用された状態になるというルールがあるので、下記のようなのもコンパイル可能です。

final int a = ("nu" + 'l' + (char) 0x6c == "null") ? 1 : -1;
char c = a;
  1. 「"nu" + 'l' + (char) 0x6c」は文字列型の定数式 "null" に解決される
  2. 「"null" == "null"」はboolean型の定数式 true に解決される
  3. 「true ? 1 : -1」はint型の定数式 1 に解決される

ここまではおおむね予想通りだったのですが、「プリミティブ型のリテラルと文字列リテラルは定数式」をよく見ると、nullリテラルが含まれていないんですよね。ここではまってました。

final int a = (null != "null") ? 1 : -1;
char c = a;

普通わかるだろうに。

でもさすがにこれはひどい

final int a = (1 / .0 + "?" == "Infinity?") ? 1 : -1;
char c = a;
  1. 「1 / .0」 は正の無限大を表すdouble型の定数式に解決される
  2. 「1 / .0 + "?"」は「"Infinity" + "?"」→「"Infinity?"」という文字列型の定数式に解決される←これはひどい
  3. 以下略

この"Infinity"という文字列、どこで規定されてるのか探していると、Java言語仕様の次の個所にありました。

A value x of primitive type T is first converted to a reference value as if by giving it as an argument to an appropriate class instance creation expression:

* If T is boolean, then use new Boolean(x).
* If T is char, then use new Character(x).
* If T is byte, short, or int, then use new Integer(x).
* If T is long, then use new Long(x).
* If T is float, then use new Float(x).
* If T is double, then use new Double(x).

This reference value is then converted to type String by string conversion.

Now only reference values need to be considered. If the reference is null, it is converted to the string "null" (four ASCII characters n, u, l, l). Otherwise, the conversion is performed as if by an invocation of the toString method of the referenced object with no arguments; but if the result of invoking the toString method is null, then the string "null" is used instead.

String conversion

まとめると「double型の値xに文字列変換をかけるときは、new Double(x).toString()が呼ばれたのと同じ効果」ということらしいです。プリミティブ型の動作を何でAPI仕様で規定するのか不明。

m が無限大の場合は、"Infinity" という文字列で表す。つまり、正の無限大は "Infinity"、負の無限大は "-Infinity" となる

Double.toString()

そーですか。

*1:正確には、定数式の評価値が代入先の型に確実に代入可能である場合のみ。char c = -1 とかはダメ