はじめに
こんにちは、私です。
前回のJava8→9編では、jshell や List.of() など、Java9の新機能を一緒にレベルアップしました。
今回はその続き。Java9→10編です。
Java10は短命リリース(LTSではない)なので、「えっこんだけ?」と思うかもしれません。でも、その分だけ1つの機能がぶっ刺さる回になっています。
お察しの方もいると思いますが——そう、var の話です。
「変数名の前に型を書かなくていい? それって……ローカル変数型推論能力(ヴァリアブル・インフェランス)ってことか?」
まあ、そんな感じです。やっていきましょう。
対象読者
- Java8→9編を読んだ方(または、Java9の概要を知っている方)
-
varを聞いたことはあるが、どこで使えてどこで使えないか曖昧な方
環境
| 項目 | バージョン |
|---|---|
| Java | 10(確認: java -version) |
| 前回 | Java 9 |
Java9からJava10へのレベルアップ
1. ローカル変数型推論 — var
Java10の目玉、そしてこの記事のほぼすべてです。
これだけでも十分だったかも?
var とは何か
ローカル変数の型宣言を、コンパイラに推論させる構文です。
// Java9以前
String message = "こんにちは";
List<String> names = new ArrayList<>();
// Java10以降
var message = "こんにちは"; // String と推論される
var names = new ArrayList<String>(); // ArrayList<String> と推論される
コンパイル時に型が確定するので、動的型付けではありません。コンパイラが「右辺を見れば型は分かるだろ」と言っているだけです。
どこで使えるか(超重要)
var はどこでも使えるわけではありません。ローカル変数(メソッド内の変数)に限定されます。
// ✅ 使える — ローカル変数
void example() {
var count = 10;
var list = List.of("a", "b");
for (var item : list) {
System.out.println(item);
}
}
// ❌ 使えない — フィールド(クラス変数)
class MyClass {
var name = "田中"; // コンパイルエラー
}
// ❌ 使えない — メソッドの引数
void doSomething(var input) { // コンパイルエラー
}
// ❌ 使えない — 戻り値の型
var getName() { // コンパイルエラー
}
初期化が必須
var は右辺から型を推論するため、宣言と同時に初期化しなければなりません。
// ❌ コンパイルエラー
var x;
x = 10;
// ✅ OK
var x = 10;
null だけでは使えない
// ❌ コンパイルエラー(型が推論できない)
var obj = null;
// ✅ キャストすれば OK
var obj = (String) null;
実務でどう使うか
長い型名を短く書けるのが一番の恩恵です。特にジェネリクスが絡むと効果抜群です。
// Java9以前(型名が長い…)
Map<String, List<Integer>> scoreMap = new HashMap<String, List<Integer>>();
// Java10以降(右辺で型は分かるので左辺はvarに)
var scoreMap = new HashMap<String, List<Integer>>();
ただし、可読性が落ちる使い方はやめましょう。
// 😕 右辺を見ても型が分かりにくい❌
var result = process(); // process() が何を返すか一目で分からない
// 👍 型が明白な場合に使う
var user = new User("田中", 30); // User であることは明らか
var はあくまで「型が自明なときの省略」です。「型を隠す」のが目的ではありません。
2. コレクションのコピー — List.copyOf() / Map.copyOf() / Set.copyOf()
Java9で List.of() / Map.of() / Set.of() が追加されましたが、Java10では 既存のコレクションから不変コピーを作る copyOf() が追加されました。
// 変更可能なリストを作る
var mutableList = new ArrayList<>(List.of("A", "B", "C"));
mutableList.add("D");
// 不変コピーを作る
var immutableCopy = List.copyOf(mutableList);
// immutableCopy.add("E"); // → UnsupportedOperationException
System.out.println(immutableCopy); // [A, B, C, D]
List.of() との違い
List.of(...) |
List.copyOf(collection) |
|
|---|---|---|
| 入力 | 個々の要素 | 既存コレクション |
| nullの扱い | null要素を含むと例外 | null要素を含むと例外 |
| 用途 | 新規作成 | 既存リストのスナップショット |
実務では「このメソッドの外にリストを渡したいが、元のリストは変えられたくない」というときに copyOf() が効きます。
3. Optional の orElseThrow()(引数なし版)
Java8で登場した Optional に、Java10で 引数なしの orElseThrow() が追加されました。
// Java8〜9の書き方(やや冗長に書くしかなかった)
var name = Optional.ofNullable(user.getName())
.orElseThrow(() -> new NoSuchElementException());
// Java10以降(引数なしで同じ意味)
var name = Optional.ofNullable(user.getName())
.orElseThrow(); // 値がなければ NoSuchElementException をスロー
地味な変更ですが、「あれ、引数なしで書けなかったっけ?」と迷う時間がゼロになります。
その他の変更(軽め)
- アプリケーションクラスデータ共有(AppCDS)の改善: JVMの起動時間短縮に関わる機能。実感できるのは大規模アプリ。
- 並列 GC のインターフェース整備: GC(ガベージコレクタ)の内部リファクタリング。アプリ開発者が直接意識することは少ない。
- スレッドローカルハンドシェイク: JVM内部の改善。アプリコードからは基本的に不透明。
実務でJava10に上げるときの注意
Java10はLTSではないため、「10で止まる」という選択肢は基本的にありません。10に上げる場合、次のLTSである11への通過点として扱うのが現実的です。
私自身の実務(WebEntryのJava 8→21アップグレード)では、8から21へ直接移行していました。10や11で「変わったこと」の一覧を知らないまま乗り越えていたわけです。この連載はその棚卸しでもあります。「飛び級でアップグレードした人間の振り返り」だと思って読んでいただけると、味わいが増すかもしれません。
おわりに
Java10の目玉は var です。以上です(清々しいくらいシンプルな回)。
ただ、var は使い方を間違えると可読性を下げる諸刃の剣でもあります。「型が自明なとき」「右辺を見れば分かるとき」だけ使うのが吉です。
次回は Java10→11編 です。Java11はLTSなので機能が増えます。標準 HttpClient、文字列の新メソッド群、単一ファイル実行など、実務で効く変化が揃っています。一緒にレベルアップを目指しましょう。
→ [Java10→11編](公開後にリンクを追加します)