2026年2月8日日曜日

【Java進化史 第4回】switch文と設計思想 ― 分岐の向こう側にあるもの

【Java進化史 第4回】switch文と設計思想 ― 分岐の向こう側にあるもの

そういえば、最近あまりswitch文を書いていない。

書けなくなったわけではない。 むしろ、書こうと思えばすぐ書ける。

だが設計を考え始めると、 どこかで立ち止まる自分がいる。


■ 手続き的思考の安心感

switchは安心だ。

条件を並べれば、 処理の流れは一目で追える。

どこで何が起きるのか、 すべてが自分の視界の中にある。

長く手続き的なコードを書いてきた身には、 この感覚は心地よい。

分岐を書く。 処理を書く。 上から順に読む。

世界が、整然と並んでいる。


■ ふと浮かぶ違和感

だがあるとき、ふと思った。

オブジェクト指向言語で、 switchをあまり見ないのはなぜだろう。

書けるのに、 あまり使われない。

それは構文の問題ではない。 設計思想の違いだ。


■ 責務という考え方

オブジェクト指向では、 振る舞いはクラスに持たせる。

条件で分けるのではなく、 責務で分ける。

「どの種類か」で分岐するのではなく、 「誰が責任を持つのか」を考える。

その結果、 分岐はオブジェクトの内部に吸収される。

外側から見えるのは、 ただの振る舞いだけになる。


■ 変更点の局所化

switchを書き始めると、 同じ条件分岐が別の場所にも現れることがある。

仕様変更が入るたびに、 その条件を探して回る。

変更点が、散らばる。

一方で、責務を適切に分けた設計では、 変更は一か所に閉じ込められる。

変更点の局所化。

それは地味だが、 長く保守する現場では大きな意味を持つ。


■ 拡張に開き、修正に閉じる

新しい種類が増えたとき、 switchはcaseを増やす。

既存のコードを修正する。

だが理想とされる設計は、 拡張に開き、修正に閉じる。

既存部分に手を入れず、 新しい要素を追加することで対応する。

それがOCP(Open/Closed Principle)と呼ばれる考え方だ。

理屈は理解している。

だが、実践は簡単ではない。


■ 設計の重さ

責務を分ける。

将来の変更を想像する。

インターフェースを定義する。

switch一つで済む場面でも、 設計は少し重くなる。

正直に言えば、 急いでいるときほど、 switchの安心感に戻りたくなる。

条件を書けば、すぐに動く。

目の前の問題は、すぐ解決する。


■ 未来の自分への負債

だが安易な分岐は、 未来の自分への負債になることがある。

仕様が増え、 条件が増え、 分岐が増え、 修正箇所が増える。

そのとき向き合うのは、 過去の自分の判断だ。

設計とは、 未来の自分との対話なのかもしれない。


■ 分岐の向こう側

switchを書かなくなったのは、 構文の問題ではない。

分岐よりも、 責務を考える時間が増えただけだ。

それが成長なのか、 ただ慎重になっただけなのかはわからない。

だが少なくとも、 設計という重みを意識するようにはなった。

分岐の向こう側にあるもの。

それを考えることが、 オブジェクト指向という思想なのだと思う。





【Java進化史 第3回】Java7 - switch文は何を守り、何を変えなかったのか?

【Java進化史 第3回】Java7 - switch文は何を守り、何を変えなかったのか?

Java7には、もう一つ地味な変更がある。

switch文で String が使えるようになった ことだ。


■ Java6までのswitch

switchで使えるのは、 int や enum などの限定的な型だけだった。


switch (statusCode) {
    case 0:
        ...
        break;
    case 1:
        ...
        break;
}

文字列で分岐したい場合、 if-else を使うことが多かった。


if ("OK".equals(status)) {
    ...
} else if ("ERROR".equals(status)) {
    ...
}

あるいは、enumを用意して対応する。

少し回り道が必要だった。


■ Java7での変更

Java7からは、 Stringがswitchで使えるようになった。


switch (status) {
    case "OK":
        ...
        break;
    case "ERROR":
        ...
        break;
}

可読性は確かに上がる。

素直に書けるようになった。

これは小さいが、現場では確実に効く変更だ。


■ だが、変わらなかったものがある

それが fall-through だ。

breakを書かなければ、 次のcaseへ処理が流れる。


switch (level) {
    case 3:
        log("HIGH");
    case 2:
        log("MEDIUM");
    case 1:
        log("LOW");
        break;
}

これはC言語由来の仕様。

Javaはここを変えなかった。


■ 他言語との違い

近年の言語では、 fall-throughは基本的に採用されていない。

  • Kotlin → 自動で終了(break不要)
  • Swift → 自動で終了
  • Rust → fall-throughなし

一方で、CやC++では明示的なbreakが必要だ。

breakを書き忘れてバグになる。

経験のある人も多いのではないだろうか。


■ なぜJavaは残したのか?

理由は明確だ。

後方互換性

既存コードを壊さない。

Javaはこの原則を徹底している。

そしてもう一つ。

Javaは「削除」ではなく 「追加」で進化する言語だからだ。

古い構文は残す。

その上で、新しい選択肢を足していく。


■ C言語世代の安心感

50代後半以上のエンジニアの多くは、 C言語からキャリアを始めたのではないだろうか。

私もその一人だ。

fall-throughを見ると、 どこか安心する。

「ああ、これはCと同じだ」と。

JavaがCに似ている部分を残しているのは、 偶然ではない気がする。

だが同時に、 言語設計は少しずつ安全性と明確さへ向かっている。

守るものと、変えるもの。

そのバランスの上に、 Javaの進化はある。


■ まとめ

Java7のswitch変更は、 革命ではない。

だが、

Javaは「何を変えないか」を選び続けている。

それがJavaらしさだ。

守りながら、少しずつ進む。

それがこの言語の進化の形なのだと思う。





【Java進化史 第2回】Java7 - diamond演算子は何を変えたのか?

【Java進化史 第2回】Java7 - diamond演算子は何を変えたのか?

最初に見たとき、正直こう思った。

「なんじゃこりゃ?」

「そもそも演算子なのか?」

「三項演算子に似てるな…?」

だが、使ってみると――

地味に、楽。


■ Java6までの書き方


List<String> list = new ArrayList<String>();

型を2回書く。

宣言側と、インスタンス生成側。

Genericsが導入されたJava5以降、 この「2回書き」は当たり前だった。

特に型が長くなると、地味に辛い。


■ Java7で何が変わったのか


List<String> list = new ArrayList<>();

右側の型が消えた。

<> だけ。

これが diamond演算子 と呼ばれる。

見た目がダイヤモンドに見えるからだ。


■ 何が嬉しいのか?

  • 記述が短くなる
  • 可読性が上がる
  • 型変更が楽になる

例えば、


List<Integer> list = new ArrayList<Integer>();


List<Long> list = new ArrayList<Long>();

に変えるとき、 2箇所修正が必要だった。

diamondなら1箇所で済む。

これは地味に効く。


■ 本当に「演算子」なのか?

実は、厳密には演算子ではない。

Genericsの型推論を コンパイラに任せるための構文だ。

つまり、

型推論の第一歩


■ 2記事書いて感じたこと

正直に言うと、 私はこれまでJava7を体系的に学んだことがなかった。

今回あらためて調べ、 第1回のtry-with-resources、 そして今回のdiamond演算子を追ってみて思った。

Java7は派手ではない。

だが、

開発者の「面倒くささ」を確実に減らしている。

例外処理の煩雑さを減らし、 Genericsの重複記述を減らし、 小さなストレスを一つずつ削っている。

革命ではない。

だが、 確実に優しい進化だ。

この2本を書いてみて、 Java7は「地味だけど開発者想いのバージョン」だったのではないか、 と感じ始めている。




【Java進化史 第1回】Java7 - try-with-resourcesは何を変えたのか?

【Java進化史 第1回】Java7 - try-with-resourcesは何を変えたのか?

Java7は地味だと言われる。

だが、現場を救った機能がある。

それが try-with-resources だ。


■ 昔のclose地獄

Java6まで、リソースを扱うコードはこうだった。


FileInputStream fis = null;
try {
    fis = new FileInputStream("test.txt");
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (fis != null) {
        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

何度これを書いただろう。

  • close忘れ
  • 二重try
  • ネスト地獄

そして一番怖いのは、 close忘れによるDB接続リークだ。

Javaから接続しているDBコネクションが解放されず、 接続数がMAXに達する。

ある日突然、全体が止まる。

原因は、たった一行のclose忘れ。

実際に、トラブルの火種になり得る。


■ JDBCコネクションプールとの関係

「でも今はコネクションプールがあるから大丈夫では?」

確かに、最近の現場では HikariCPなどのコネクションプールを使うのが普通だ。

だが重要なのはここだ。

close()は“破棄”ではない。

プール利用時のclose()は、 接続を物理的に切断するのではなく、 プールへ「返却」しているだけだ。

つまり、close()を呼ばなければ、 プールに戻らない。

結果として、

  • 接続が枯渇する
  • 待ちが発生する
  • 最悪、システム停止

try-with-resourcesは、 プール利用環境でも極めて重要なのだ。


■ 実務では自分で書かない?

正直に言うと、 DB接続処理などは共通部品化されていることが多い。

だから普段は、自分でcloseを書くことは少ない。

だが――

「ちょっとした検証コード」
「一時的なバッチ」
「ローカルでのテスト」

こういうときに、自分で書く。

そして、うっかりcloseを書き忘れる。

私はある。


■ Java7で何が変わったのか


try (FileInputStream fis = new FileInputStream("test.txt")) {
    // 処理
} catch (IOException e) {
    e.printStackTrace();
}

tryの丸括弧の中に「resource」を書く。

すると、ブロック終了時に自動でclose()が呼ばれる。

finallyは不要。

ネストも不要。


■ 名前の意味

try-with-resources。

直訳すれば、

「リソース付きtry文」。

リソース(=closeが必要なもの)を tryと一緒に宣言する。

だから、try with resources。


■ AutoCloseableとは何か

対象となるのは、 AutoCloseable を実装しているクラスだ。

つまり「close()メソッドを持つ型」。

  • ファイル(FileInputStream / BufferedReader など)
  • データベース(Connection / Statement / ResultSet)
  • ソケット(Socket)
  • ZipFile
  • Scanner

JDK標準ライブラリの多くはAutoCloseableに対応している。

「closeが必要なものは大体いける」と思ってよい。

さらに、自作クラスでも実装できる。


class MyResource implements AutoCloseable {
    public void close() {
        System.out.println("closed");
    }
}

これは単なる糖衣構文ではない。

リソース管理の統一ルールを作った機能なのだ。


■ Java6方式と混在しても動くのか?

結論から言えば、動く。

だが、保守性は確実に落ちる。

  • 書き方が統一されない
  • レビュー時の負担が増える
  • ミスの温床になる

可能であれば段階的に置き換えるべきだ。

もちろん、 予算と時間が許せば。

だが新規コードでは、必ず使う。


■ まとめ

派手さはない。

ラムダのような衝撃もない。

だが、

try-with-resourcesは、 「書きやすくした機能」ではない。

事故を減らすための設計思想だ。

Javaは派手に変わることもある。
だが、本当に現場を救うのは、
こういう地味な進化かもしれない。





【Java実行環境】ブラウザだけでJavaは動くのか?paiza.ioを試してみた

【Java実行環境】ブラウザだけでJavaは動くのか?paiza.ioを試してみた

前回、MacにJDK21をインストールした。

Homebrewで入れて、HelloWorldまで動かした。

「よし、これで本格的にやるぞ」

…と思ったが、ふと考えた。

今って、ブラウザだけでJava動かせないのか?


■ 若い頃は、こんなサービスなかった

私が若い頃。

Javaを動かすには、必ず環境構築が必要だった。

  • JDKをダウンロード
  • パスを通す
  • コンパイルする

環境が壊れたら、半日つぶれる。

それが普通だった。

でも今は違う。

ブラウザを開くだけで、Javaが書ける。

いい時代になった。


■ paiza.ioを使ってみる(無料)

今回は、以前少し触ったことのある paiza.io を使ってみる。

https://paiza.io/ja/projects/new

無料で使えるオンライン実行環境だ。

特別なインストールは不要。

ブラウザでアクセスするだけ。


■ 使い方(超かんたん)

上のURLにアクセスすると、画面左上に緑色のボタンがある。

そこで言語を Java に変更する。

すると、次のようなテンプレートが表示される。


import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        // Your code here!
        
        System.out.println("XXXXXXXX");
    }
}

この


System.out.println("XXXXXXXX");

を、次のように書き換える。


System.out.println(System.getProperty("java.version"));

そして、左中ほどにある緑色の「実行」ボタンを押す。

すると、下の実行結果エリアに、Javaのバージョンが表示される。

(今日時点では)


18.0.2

と表示された。

ローカルに何も入れていなくても、Javaが動く。

正直、ちょっと感動する。


■ 昔のJavaでも動くのか?

ちなみに、


System.getProperty("java.version")

は、かなり昔のJavaから使えるメソッドだ。

Java6でも問題なく動く。

こういう「昔からあるAPI」を使うと、世代を超えて検証できるのが面白い。


■ 実際に使ってみた感想

いいところ:

  • すぐ試せる
  • 環境構築が不要
  • 無料で使える
  • ちょっとした検証には最適

気になるところ:

  • 実行がやや遅い
  • ブラウザなので入力スピードが出ない
  • コード補完などの支援は弱い

本気で開発するには少し厳しい。

でも、

「ちょっと試したい」
「若手のコードの一部を検証したい」

そんな用途には、十分すぎる。


■ ローカル環境との違い

前回、MacにJDK21をインストールした。

ローカル環境では:

  • 高速に実行できる
  • 複数バージョンを切り替えられる
  • 本格的な開発ができる

一方、ブラウザ環境は:

  • とにかく手軽
  • 準備ゼロ
  • すぐ試せる

用途が違う。

どちらが正しい、ではない。


■ まとめ

Javaは、昔よりもずっと始めやすくなっている。

環境構築で挫折する時代ではない。

でもやはり、本格的にやるならローカル環境は必要だ。




【Java環境構築】MacにHomebrewでJava21をインストールしてHello Worldまで動かす

【Java環境構築】MacにHomebrewでJava21をインストールしてHello Worldまで動かす

若手のコードが読めなかったあの日から、再学習を始めた。

まずは環境を整えるところからやる。


■ 前提環境

  • Mac OS 15.6.1
  • Homebrew 5.0.13
  • Java未インストール状態から開始

Homebrewのバージョン確認:

% brew --version
Homebrew 5.0.13

今回はJava21を使用する。

最新を追いかけるのではなく、LTSであり、現場利用も多い安定バージョンから再出発する。


■ Javaが入っていないことを確認

javac --version

未インストールの場合:


The operation couldn’t be completed. Unable to locate a Java Runtime.
Please visit http://www.java.com for information on installing Java.

よし。まっさらだ。


■ HomebrewでJava21をインストール

brew install --cask temurin@21

インストール後の確認:

javac --version

出力:


javac 21.0.10

21が入った。

数字を見ると、少し安心する。


■ JAVA_HOMEを設定する

~/.zshrc に追加する。


export JAVA_HOME=$(/usr/libexec/java_home -v 21)

反映:

source ~/.zshrc

■ Hello Worldで動作確認

Hello.java を作成:


public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, Java21!");
    }
}

コンパイル:

javac Hello.java

実行:

java Hello

出力:


Hello, Java21!

ようやく戻ってきた感じがした。


■ 実は一度つまずいた

javac Hello

エラー:


エラー: クラス名'Hello'が受け入れられるのは、注釈処理が明示的にリクエストされた場合のみです
エラー1個

.java を付け忘れていただけだった。

こういうところで止まるのが、今の自分だ。


■ インストール済みJDK確認

/usr/libexec/java_home -V

複数バージョンを入れるときに使う。


■ まとめ

Java21(21.0.10)の導入と動作確認が完了。

小さな一歩だが、確実に前進している。





2026年2月7日土曜日

若手に聞かれて固まった。「JavaのLTS」って何だ?JDKの違いも整理してみた

若手が言っていた「JavaのLTS」って何?そしてJDKの違いも整理してみた

ある日、若手との会話でこんな言葉が出てきた。

「今は21がLTSですよね?」

……LTS?

聞いたことはある。でも説明できない。

正直、頭に浮かんだのはこれだった。

「LTEなら昔聞いたことがあるけど、そのこと?」

スマホの通信規格じゃない。それは違う。

でも、そのくらい曖昧だった。


① Javaのバージョンはどう変わったのか

私の感覚はこうだった。

Java5 → Java6 → Java7

数年おきに大きく変わる。

そんなペースだったはず。

しかしJava9以降、リリース方式が変わった。

半年ごとに新バージョンが出る。

9、10、11、12……

数字はどんどん進む。

気づけば25。

「そんなに出ているのか?」

正直、追えていなかった。


② LTSとは何か

LTSとは Long Term Support の略。

長期サポート版、という意味らしい。

半年ごとに出る通常版とは違い、
長期間サポートされる安定版 がLTSだ。

企業で採用されやすいのは、このLTS。

最近のLTSは次の通り。

  • Java8
  • Java11
  • Java17
  • Java21

なるほど。


だから若手は普通に「LTS」という言葉を使っていたのか。


③ OpenJDKとOracle JDKの違いは?

さらに混乱したのが、JDKの種類だ。

  • Oracle JDK
  • OpenJDK
  • Eclipse Temurin
  • Amazon Corretto

何が違うのか。

■ 機能は同じなのか?

同じバージョンであれば、基本的な機能は同じ。

Java21なら、言語仕様や標準APIは基本的に共通。

コードの動作そのものに大きな差はない。

■ 違いは何か?

主な違いは次の3つ。

  • ライセンス
  • サポート体制
  • 提供元

OpenJDKはオープンソースで基本無料。

Oracle JDKはOracleが提供するJDK。
現在は多くの場合無料で利用できるが、
企業向けには有料サポート契約がある。

つまり、機能差というより


サポートと契約の違いが本質らしい。


④ 企業ではどちらが使われているのか

最近の企業はどちらを使っているのか?

答えは――

混在している。

一時期、Oracle JDKのライセンス変更が話題になった。

それをきっかけに、OpenJDK系へ移行する企業が増えた。

クラウド環境では、

  • Amazon Corretto
  • Eclipse Temurin

などを採用する例も多い。

一方で、既存システムではOracle JDKが使われ続けていることもある。

現場は統一されているわけではなく、


移行中・混在状態というのが実情らしい。


⑤ なぜ今回はJava21でいくのか

最初は最新バージョンで始めようかとも思った。

しかし、やり直しの目的を考えた。

  • 現場で通用すること
  • 長く使われるバージョンであること
  • 情報が安定していること

それを考えると、


Java21(LTS)がちょうどいい。

いきなり最先端を追いかけるのではなく、
まずは足場を固める。

今回はJava21 + Oracle JDKで環境を作る。


まとめ

LTS。

聞いたことはあった。

でも、説明できなかった。

JDKの違いも、なんとなくで流していた。

知らないのが怖いのではない。

曖昧なままにしていることが怖い。

次回は、MacにJava21(Oracle JDK)をインストールする。

まずは動かす。




【Java進化史 第4回】switch文と設計思想 ― 分岐の向こう側にあるもの

【Java進化史 第4回】switch文と設計思想 ― 分岐の向こう側にあるもの そういえば、最近あまりswitch文を書いていない。 書けなくなったわけではない。 むしろ、書こうと思えばすぐ書ける。 だが設計を考え始めると、 どこかで立ち止まる自分がいる。 ...