Javaキャスティング:コンパイラが間違っているのか、それとも言語仕様が間違っているのか、それとも私は間違っているのか?
質問
私はJava Language Spec、第3版を読んでいますが、仕様とJavacコンパイラの実装の矛盾だと思うものを見つけました。 Eclipseコンパイラには同じ矛盾があります。
セクション 15.16 キャスト表現について話します。引数タイプをキャスト変換を介してキャストタイプに変換できない場合は、コンパイル時間エラーである必要があると述べています(セクション5.5):
オペランドのコンパイル時間タイプが、キャスト変換のルール(§5.5)に従ってキャストオペレーターによって指定されたタイプにキャストされない場合、コンパイル時間エラーです。それ以外の場合、実行時に、キャストオペレーターによって指定されたタイプに変換をキャストすることにより、オペランド値が(必要に応じて)変換されます。
セクション 5.5 キャストの変換について話します。許可されている変換タイプのリストを提供します。特にリストに存在しないことは、「ボックス化変換を解除し、それに続く原始変換の拡大/狭窄」です。 でも その正確な変換シーケンスは、Javacコンパイラ(およびEclipseコンパイラ)によって許可されているようです。例えば:
long l = (long) Integer.valueOf(45);
...うまくコンパイルされています。 (問題のあるキャストはキャストです long
;引数はタイプです java.lang.Integer
, 、したがって、変換にはボックス化解除が必要です int
その後、原始変換が拡大します)。
同様に、JLSによると、からキャストすることはできないはずです byte
に char
, 、それは(によると 5.1.4)プリミティブ変換が拡大する必要があります と 狭くなる原始的な変換 - しかし、このキャストはコンパイラーによっても許可されています。
誰かが私を啓発できますか?
編集: これを尋ねて以来、私はaを提出しました バグレポート オラクルと。彼らの反応は、これが「JLSのグリッチ」であるということです。
解決
私はあなたが正しいと思います、コンパイラーは正しいです、そしてスペックは間違っています....
これはコンパイル: (Object)45
そして、これは次のとおりです。 (Long)45
コンパイラの動作(私が使用しているIntellijを含む)を理解する唯一の方法は、キャスト変換が割り当て変換とメソッドの呼び出し変換に同意するように変更されている場合です。
ボクシング変換(§5.1.7)に続いて、参照変換が拡大します
除外変換(§5.1.8)に続いて、プリミティブ変換が拡大します。
プラス
- 原始的なコネルの拡大と狭窄
スペックは、「キャスト変換は割り当てまたはメソッドの呼び出し変換よりも包括的です。キャストは、文字列変換またはキャプチャ変換以外の許可された変換を行うことができます」
他のヒント
私の読書によって、キャスト int
に long
この条項で許可されています:
プリミティブタイプの値は、タイプが同じ場合、または拡大する原始変換または狭くなる原始変換によって、アイデンティティ変換によって別のプリミティブタイプにキャストできます。
変換 int
に long
aです 原始変換の拡大.
それはただ変換を離れます Integer
に int
, 、最後の弾丸に対応しています:
ボックス化変換
もちろん、キャスト long
例では必要ありません。
次の4つの定義を検討してください。
final Integer io = Integer.valueOf(45);
final int i = io;
final long l1 = (long)i;
final long l2 = i;
それらのいずれかが驚くべきことだと思いますか?あなたの元の例は何も変わらない。単に中間変数を排除するだけです。