varによるローカル変数型推論のコードトレース

初期化式に演算やメソッド呼出しを含めたとき、推論される型は何かを追跡します。小さな値だからbyteになる、実体が文字列だから全メソッドを呼べる、といった誤解を実行結果で解きます。

この単元で学ぶこと

  • 初期化式から固定される型を求める
  • varを使えない宣言を判定する
  • ループ・参照・配列での型推論を追う

本文解説

式の結果を先に決める var x=5/2;では初期値の計算がintの2なのでxはintです。var x=5/2.0;ならdoubleの2.5です。var x=5/2;System.out.print(x+0.5);は2.5ですが、初期値の失われた小数部分が復元されたわけではありません。

小さい整数でも通常はint var n=1;は値がbyte範囲内でもintです。var n=(byte)1;ならキャスト式からbyteになります。ただしbyte同士の加算は通常intへ数値昇格するため、var sum=(byte)1+(byte)2;のsumはintです。値の範囲と式の型を別に記録します。

複合代入の暗黙変換 var n=(byte)127;n+=1;は複合代入に含まれるbyteへの変換で-128になります。一方n=n+1;は右辺がintなのでキャストなしではコンパイルできません。varが特殊な変換をするのではなく、推論されたbyteに通常の代入規則を適用します。

メソッドの戻り値も型情報 var a=Integer.parseInt("12");はint、var b=Integer.valueOf("12");はIntegerです。a.intValue()は呼べませんがb.intValue()は呼べます。両方をprintすると12でも、値の表示だけでは型は区別できません。

型を指定したnullと再代入 var s=(String)null;s="ok";は有効です。最初にString型が定まり、その後Stringへの代入を許します。s=null;へ戻すこともできます。var n=0;n=null;はintにnullを代入できず失敗します。初期化時と代入時の二段階で考えます。

コレクションの型引数 var a=new java.util.ArrayList<String>();なら要素はStringです。a.add(1)は許されません。var b=new java.util.ArrayList<>();ではこの初期化式に代入先からの型指定がなく、要素型Objectとなるので文字列とIntegerの両方を追加できます。varは必要な型引数まで常に推測してくれるわけではありません。

スコープは拡大しない for(var i=0;i<2;i++){}のiはfor文の外では利用できません。また同じブロックで既に宣言されたローカル変数をvarで再宣言することもできません。型名を省略する機能に、名前の重複や見える範囲を変更する効果はありません。

宣言構文と式を分ける var a[]=new int[2];は宣言側の角括弧が許されず、var a=new int[2];に直します。var f=()->1;も、そのラムダ式の対象となる関数型が決まらず不正です。必要ならjava.util.function.IntSupplier f=()->1;と型を明示します。型推論は情報不足を解消する魔法ではありません。

図解

varから操作可能な型へ
値が変わることと型が変わることは別。

重要ポイント

  • 型は初期化式に基づきコンパイル時に決まる。
  • 初期値なし・nullだけ・複数変数の同時宣言は使えない。
  • 推論された型と代入される実際の値を区別する。

間違えやすい点

  • varはObjectの別名でも動的型でもない。
  • 配列初期化子だけではなくnewと型が必要。
  • varで宣言してもfinalが自動で付くわけではない。

用語解説

型推論
式の情報からコンパイラが変数の型を決めること。
初期化式
変数宣言時に初めの値を与える式。
静的型
実行前の型検査に使う型。varでも固定される。

関連教材

参考資料

教材データに登録された参考リンクです。リンク先の内容や更新日は提供元でご確認ください。

確認問題の出題・採点や学習履歴の保存にはログインが必要です。