
メンテナンスなどで過去のJavaソースコードを見ることがあるという人も多いのではないでしょうか。この記事ではレガシーなJavaコードの書き方と現代の比較についてお伝えします。
- 変数の宣言
- 文字列を “”” (トリプルクォーテーション) で囲むと超便利
- 便利になった switch 文
- Java16 までなかった record
- 不変性 と List.of()
- Stream API処理の変遷
変数の宣言
Java 8 までは変数を宣言するときには、必ず型を宣言する必要がありました。今のJavaは右辺から型が明らかな場合、var と書くだけで型を自動推論してくれます。
// Java 8までの書き方
String message = "ありがとう";
List<String> list = new ArrayList<>();
// 現代の書き方
var modernMessage = "ありがとう";
var modernList = new ArrayList<String>(); // 右辺からStringのリストと推論される
型とは、データの種類とどんな操作ができるのかを分類するラベルのようなものです。上記例でいう String, List<String> になります。
文字列を “”” (トリプルクォーテーション) で囲むと超便利
SQL文やJSON、あるいは長い文章をコード内に書くとき、Java 8までは + で文字列を結合したり改行コード (\n) を挟んだりして、非常に見づらいコードになっていました。これが Java 15 からはトリプルクォーテーション (“””) で囲むだけでそのまま書けるようになりました。昔のJavaに比べて、文字列処理のストレスが大幅に減っています。
// トリプルクォーテーション (""") の書き方(改行やインデントがそのまま保持されます)
var html = """
<html>
<body>
<p>Hello, Modern Java!</p>
</body>
</html>
""";
System.out.println(html);
旧システムのメンテなどで、昔の Java コードを扱う人は覚えておくとよいでしょう。
便利になった switch 文
Java 8までの `switch` 文は、`break;` を書き忘れてバグを生んだり、値を返すためにわざわざ外側で変数を用意したりと、少し不格好でした。Java 14 からは、値を直接返せる「式」になり、矢印(`->`)を使うことで `break` が不要になりました。
var day = "TUESDAY";
// switchが値を返す式になり、breakが不要になりました
var result = switch (day) {
case "MONDAY", "FRIDAY" -> "出社";
case "SATURDAY", "SUNDAY" -> "休日";
default -> "在宅ワーク";
};
System.out.println(result);
上記のように月曜と金曜は出社、火曜・水曜・木曜は在宅ワーク、土日は休日という switch 文を簡単に記述することができます。
Java16 までなかった record
古いJavaコードでは、DTOやEntityなどのデータを保持するためだけのクラスを作る際、フィールド定義、ゲッターや toString()、equals()、hashCode() などを大量に書く必要があり、コードが肥大化しがちでした。今の Javaでは、record という仕組みを使い、わずか1行でこれらを自動定義できるようになりました。
旧
package hello;
import java.util.Objects;
/**
* 古い時代の典型的なデータ保持クラス(DTO)
*/
public class ProductJava8 {
// 1. カプセル化のための不変(private final)フィールド
private final String name;
private final int price;
// 2. コンストラクタ(全フィールドを初期化する)
public ProductJava8(String name, int price) {
this.name = name;
this.price = price;
}
// 3. ゲッターメソッド(フィールド値を取得する)
public String getName() {
return name;
}
public int getPrice() {
return price;
}
// 4. toStringメソッド(デバッグ時に中身を見やすくする)
@Override
public String toString() {
return "ProductJava8{name='" + name + "', price=" + price + "}";
}
// 5. equalsメソッド(インスタンス同士の「中身(値)」が同じか比較できるようにする)
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ProductJava8 that = (ProductJava8) o;
return price == that.price && Objects.equals(name, that.name);
}
// 6. hashCodeメソッド(Mapのキーなどで正しく扱えるようにする ※equalsとセットで必須)
@Override
public int hashCode() {
return Objects.hash(name, price);
}
}
新
// これだけで、ゲッター、toString、equalsなどが自動実装されます
public record Product(String name, int price) {}
今でも record が旧時代のように定義されていることを知っておく必要があるでしょう。
不変性 と List.of()
Java 8までの時代、テストデータや固定のリストを作りたいとき、以下のような書き方をしていました。一見便利ですが、Arrays.asList() で作ったリストは、後から要素を書き換えられてしまう (list.set(0, “ミカン”) が可能) という、安全面での脆さがありました。
List.of() で作られたリストは、後から要素を追加 (add) することも、変更 (set) することも、削除 (remove) することも絶対にできない「不変リスト」になります。もし変更しようとすると、実行時にエラー(UnsupportedOperationException) が発生します。
// Java 8 までの一般的な書き方
List<String> list = Arrays.asList("リンゴ", "バナナ", "メロン");
// 現行 version の書き方
var list = List.of("リンゴ", "バナナ", "メロン");
現代の開発では、「一度作ったデータは、誰にも書き換えさせない」のがバグを減らす鉄則とされています。
複数人で開発しているときや、複雑な処理を行うときに、どこかのメソッドが勝手にリストの中身を書き換えてしまうと、原因不明のバグに繋がるからです。
Stream API処理の変遷
Stream API は、現代でも大量のデータを処理するために使われます。Java 8より前の時代は for 文と if 文を組み合わせて泥臭く書いていた処理を、「流れるような1本のパイプライン」として表現します。Stream APIの処理は、必ず以下の3つのステージで構成されます。
- ストリームの生成: リストから流れるプール (Stream) を作る。(stream())
- 中間操作(加工): データを絞り込んだり、変換したりする。(何度でも行える)
- 終端操作(結果の出力): 最終的な形にまとめる。(1回だけ行える。ここで処理が実行される)
public record Product(String name, int price) {}
// mainメソッドでの利用例
public static void main(String[] args) {
var products = List.of(
new Product("リンゴ", 150),
new Product("バナナ", 100),
new Product("メロン", 500),
new Product("柿", 200)
);
products.stream() // 1. ストリームの生成
.filter(p -> p.price() > 150) // 2. 中間操作(条件に合うものだけに絞り込む)
.forEach(p -> System.out.println(p.name())); // 3. 終端操作(1つずつ出力する)
}
Java 8時代は .collect(Collectors.toList()) と長く書く必要がありましたが、Java 16 では .toList() と書くだけで、加工済みの新しい不変リストをスマートに取得できるようになりました。
// 150円以上の商品「だけ」を詰めた、新しいリストを作る
List<Product> expensiveProducts = products.stream()
.filter(p -> p.price() >= 150)
.toList(); // 現代の非常に便利な終端操作
コメント