Java | Java 詳細・モダン文法:言語仕様詳細 – var(ローカル変数型推論)

Java Java
スポンサーリンク

var を一言でいうと

var は「型を書かなくても、右辺からコンパイラが型を推論してくれるローカル変数宣言」のためのキーワードです。
「型がなくなる」のではなく、「型を書く手間を省く」だけで、コンパイル時の静的型チェックはこれまで通り行われます。

var message = "hello";   // String と推論される
var number  = 10;        // int と推論される
Java

var を使っても Java は“静的型付き言語”のままです。
「型を省略しても、コンパイラはちゃんと型を知っている」というのが、まず一番大事なポイントです。


var が使える場所・使えない場所

ローカル変数にだけ使える

var が使えるのは「ローカル変数」だけです。
メソッドの中で宣言する変数や、for 文のカウンタなどが対象です。

void sample() {
    var text = "hello";      // OK
    var list = List.of(1,2); // OK
}
Java

フィールド、メソッド引数、戻り値の型には使えません。

class NG {
    // var name;              // コンパイルエラー(フィールドには使えない)
    // void hello(var msg) {} // コンパイルエラー(引数には使えない)
}
Java

「メソッドの外側の“型の顔”はちゃんと書く。
中の細かいローカル変数だけ、var で省略してよい」という設計になっています。

必ず「初期化」とセットで書く必要がある

var は右辺から型を推論するので、初期化なしでは使えません。

// var x;          // コンパイルエラー(右辺がないので型が分からない)
var x = 10;        // OK(int と推論される)
Java

また、右辺が null だけでもダメです。

// var value = null; // コンパイルエラー(null だけでは型が決められない)
Java

「右辺に“具体的な型を持つ式”があること」が必須条件です。


var の型はどう決まるのか

右辺の“静的型”がそのまま入る

例えば、次のようなコードを見てみます。

var list = List.of("A", "B", "C");
Java

このとき、コンパイラは list の型を List<String> と推論します。
var は「なんとなくの型」ではなく、「本来ここに書くはずだった正確な型」をそのまま当てはめます。

だから、次のようなことも普通に起きます。

var num = 1;      // int
num = 2;          // OK
// num = "hello"; // コンパイルエラー(String は代入できない)
Java

var を使っても、「型がゆるくなる」「なんでも入る」ことは一切ありません。
“型名を省略しているだけ”だと理解してください。

型推論が「読みにくさ」を生むケース

例えば、これはどうでしょう。

var result = someMethod();
Java

someMethod() の戻り値の型がコードからすぐに分かるなら問題ありませんが、
ジェネリクスが絡んでいたり、メソッド名が曖昧だったりすると、result の型がパッと見で分かりにくくなります。

var を使うときは、

「右辺を見れば型が一目で分かるか?」
「変数名から“何者か”が想像できるか?」

を自分に問いかけるのが大事です。


var が「特に効く」場面

ジェネリクスで型が長くなるとき

典型的なのは、ジェネリクスで型がやたら長くなるケースです。

Map<String, List<Integer>> map = new HashMap<>();
Java

これを var で書くと、こうなります。

var map = new HashMap<String, List<Integer>>();
Java

右辺に型引数を書いているので、コンパイラは map の型を HashMap<String, List<Integer>> と推論します。
さらに、ダイヤモンド演算子を使えば、もっとスッキリします。

var map = new HashMap<String, List<Integer>>(); // でもいいし
var map2 = new HashMap<String, List<Integer>>(); // でもいい
Java

実務では、右辺にファクトリメソッドを使うことも多いです。

var list = List.of("A", "B", "C"); // List<String>
Java

「右辺を見れば型が分かる」パターンでは、var はかなり読みやすさに貢献します。

for 文のカウンタや拡張 for

for 文でもよく使われます。

for (var i = 0; i < 10; i++) {
    System.out.println(i);
}

for (var s : List.of("A", "B", "C")) {
    System.out.println(s);
}
Java

ここでは、iintsString と推論されます。
特に拡張 for では、右辺のコレクションの型から要素型が分かるので、var との相性が良いです。


var を「使ってはいけない」わけではないが、乱用は危険

読みやすさを壊す var の例

例えば、こんなコードはどうでしょう。

var x = process(data);
var y = transform(x);
var z = handle(y);
Java

コンパイラ的には何の問題もありませんが、読む人からすると「x / y / z が何の型か」が全く分かりません。
var 自体が悪いのではなく、「変数名と右辺から型が想像できない」のが問題です。

もう少しマシにするなら、こうです。

var processedData = process(data);
var transformed = transform(processedData);
var handled = handle(transformed);
Java

それでも、「どんな型か」はメソッド定義を見に行かないと分かりません。
「ここは型を明示した方が安心だな」と感じたら、素直に型名を書いた方がいいです。

Result result = handle(transform(process(data)));
Java

「型を隠す」のではなく「ノイズを減らす」ために使う

var の目的は「型を隠す」ことではなく、「型が明らかな場面で、重複した記述を減らす」ことです。

例えば、こういうのはノイズが多いです。

List<String> names = List.of("Alice", "Bob");
Java

右辺を見れば List<String> だと分かるので、ここは var で十分です。

var names = List.of("Alice", "Bob");
Java

逆に、右辺だけでは型が分かりにくいときは、var を我慢して型を書いた方が、未来の自分が楽になります。


var と「型安全性」の関係

Java の型安全性は変わらない

もう一度強調しておきますが、var を使っても Java の型安全性は一切変わりません。

var list = new ArrayList<String>();
list.add("hello");
// list.add(123); // コンパイルエラー
Java

コンパイラは list の型を ArrayList<String> と認識しているので、Integer を入れようとすると普通に怒られます。
var は「型を省略するための構文糖衣」であって、「動的型付け」ではありません。

キャストや raw 型と組み合わせるときは特に注意

例えば、raw 型を使ってしまうと、var の推論結果も raw 型になります。

List raw = getRawList(); // raw 型
var list = raw;          // List と推論される(型引数なし)
Java

ここから先は、list に何でも入ってしまう危険な世界です。
var を使う前に、「右辺の型がちゃんとジェネリクスで型安全になっているか?」を意識する必要があります。


まとめ:var を自分の言葉でどう使い分けるか

あなたの言葉で var を整理すると、こうなります。

var は、ローカル変数の“型名を書く手間”を省くためのキーワードで、
右辺からコンパイラが正確な型を推論してくれる。
Java の静的型付けはそのままで、型安全性は一切失われない。

ただし、
・ローカル変数にしか使えない
・必ず初期化とセットで書く必要がある
・右辺や変数名から型が一目で分かる場面で使うべきで、
 型が分かりにくくなるなら素直に型名を書いた方が読みやすい。

“型を隠す道具”ではなく、“型が明らかなところでノイズを減らす道具”として使うのがちょうどいい。」

タイトルとURLをコピーしました