Java Tips | 基本ユーティリティ:OS判定

Java Java
スポンサーリンク

OS判定は「環境依存コードを一箇所に閉じ込める」技

Java は基本的にマルチプラットフォームですが、実務ではどうしても OS ごとに挙動を変えたい場面が出てきます。
例えば「Windows だけパスの区切りが違う」「Linux だけ特定コマンドを叩きたい」「Mac では一部機能を無効にしたい」といったケースです。

こういうときに、あちこちで if (osName.contains("Windows")) のような判定を書き始めると、すぐにコードが汚れます。
そこで、「OS判定」をユーティリティとして一箇所に閉じ込めてしまうと、読みやすさも保守性も一気に上がります。


OS名の取得と、素朴な判定のやり方

System.getProperty(“os.name”) の基本

Java では、システムプロパティ os.name から OS 名を取得できます。

public class OsNameSample {

    public static void main(String[] args) {
        String osName = System.getProperty("os.name");
        System.out.println("os.name = " + osName);
    }
}
Java

実行環境によって、例えばこんな値になります。

Windows なら "Windows 10""Windows 11"
Linux なら "Linux"
macOS なら "Mac OS X" など

この文字列をもとに、containsstartsWith で判定するのが基本パターンです。

そのままベタ書きすると何がつらいか

例えば、こんなコードがあちこちに出てくるとします。

String osName = System.getProperty("os.name");
if (osName != null && osName.toLowerCase().contains("win")) {
    // Windows 用の処理
}
Java

これを毎回書くのは面倒ですし、
"win" にするか "Windows" にするか、toLowerCase を付けるかどうか、null チェックをするかどうかがバラバラになりがちです。

こういう「細かいけど重要な判定ロジック」は、ユーティリティに閉じ込めてしまったほうが圧倒的に安全です。


実務で使える OS判定ユーティリティの最小形

OS種別を enum で表現する

まず、「OS を文字列ではなく型で扱う」ために enum を用意します。

public enum OsType {
    WINDOWS,
    LINUX,
    MAC,
    OTHER
}
Java

次に、os.name から OsType を判定するユーティリティを作ります。

public final class OsDetector {

    private static final String OS_NAME =
            System.getProperty("os.name", "").toLowerCase();

    private OsDetector() {}

    public static OsType detect() {
        if (OS_NAME.contains("win")) {
            return OsType.WINDOWS;
        }
        if (OS_NAME.contains("mac")) {
            return OsType.MAC;
        }
        if (OS_NAME.contains("nux") || OS_NAME.contains("nix")) {
            return OsType.LINUX;
        }
        return OsType.OTHER;
    }

    public static boolean isWindows() {
        return detect() == OsType.WINDOWS;
    }

    public static boolean isLinux() {
        return detect() == OsType.LINUX;
    }

    public static boolean isMac() {
        return detect() == OsType.MAC;
    }
}
Java

使う側はこう書けます。

if (OsDetector.isWindows()) {
    System.out.println("Windows 用の処理を実行");
} else if (OsDetector.isLinux()) {
    System.out.println("Linux 用の処理を実行");
}
Java

ここで深掘りしたいポイントは三つです。

一つ目は、「os.name の取得と小文字化を一箇所に閉じ込めている」ことです。
System.getProperty("os.name") をアプリ全体にばらまかず、ユーティリティの中だけで扱うことで、判定ロジックを後から変えやすくなります。

二つ目は、「enum で OS 種別を表現している」ことです。
"Windows""Linux" といった生の文字列ではなく、OsType.WINDOWS のような型で扱うことで、typo や比較ミスを防げます。

三つ目は、「isWindows などのメソッド名に“意図”が乗っている」ことです。
呼び出し側は if (OsDetector.isWindows()) と書くだけで、「ここは Windows だけ特別扱いしている」という意図が一目で分かります。


OSごとのパスやコマンドをユーティリティに寄せる

パス区切りや一時ディレクトリの扱い

OS ごとに違う代表例が「パス区切り」です。
もちろん File.separatorPaths.get(...) を使えば OS 依存をかなり吸収できますが、
どうしても OS ごとにパスを変えたい場面もあります。

例えば、ログ出力先を OS ごとに変えたいとします。

public final class LogPathResolver {

    private LogPathResolver() {}

    public static String resolveLogDir() {
        if (OsDetector.isWindows()) {
            return "C:\\app\\logs";
        }
        if (OsDetector.isMac()) {
            return "/usr/local/var/app/logs";
        }
        // Linux その他
        return "/var/log/app";
    }
}
Java

使う側はこうです。

String logDir = LogPathResolver.resolveLogDir();
System.out.println("ログディレクトリ: " + logDir);
Java

ここでの重要ポイントは、「OSごとの差分を“呼び出し側”ではなく“ユーティリティ側”に寄せる」ことです。
アプリのあちこちで if (OsDetector.isWindows()) と書き始めると、OS 依存が散らばってしまいます。
「パス」「コマンド」「設定値」など、OS で分岐するものは、できるだけ専用のユーティリティに閉じ込めるのがきれいです。

OSごとのコマンド実行

例えば、「Windows では cmd /c dir、Linux では ls を実行したい」といったケースも、ユーティリティにまとめられます。

public final class OsCommands {

    private OsCommands() {}

    public static String[] listDirectoryCommand(String path) {
        if (OsDetector.isWindows()) {
            return new String[] {"cmd", "/c", "dir", path};
        }
        // Linux / Mac
        return new String[] {"ls", "-l", path};
    }
}
Java

呼び出し側は OS を意識せずにこう書けます。

String[] cmd = OsCommands.listDirectoryCommand(".");
Process process = new ProcessBuilder(cmd).inheritIO().start();
process.waitFor();
Java

「どの OS でどのコマンドを使うか」という知識を、OsCommands に集約しているのがポイントです。


OS判定で気をつけるべき落とし穴

os.name の値に依存しすぎない

os.name の値は、JVM の実装やバージョンによって微妙に違う可能性があります。
例えば、将来 "Mac OS X""macOS" になるかもしれません。

そのため、判定は「完全一致」よりも「小文字化して一部を含むかどうか」で書くのが現実的です。

private static final String OS_NAME =
        System.getProperty("os.name", "").toLowerCase();

if (OS_NAME.contains("mac")) { ... }
if (OS_NAME.contains("win")) { ... }
if (OS_NAME.contains("nux") || OS_NAME.contains("nix")) { ... }
Java

このあたりの「ゆるいけれど実務的な判定ロジック」を、ユーティリティに閉じ込めておくことで、
呼び出し側は OS 判定の細かい仕様を気にせずに済みます。

「本当に OS で分岐すべきか?」を一度立ち止まって考える

OS 判定は便利ですが、「本当は標準 API で吸収できるのに、安易に OS で分岐してしまう」パターンもあります。

例えば、パスの結合は Paths.get(...)Path.resolve(...) を使えば OS 非依存で書けますし、
一時ディレクトリは Files.createTempDirectory(...) で OS を意識せずに扱えます。

OS 判定ユーティリティを持つこと自体は悪くありませんが、
「まずは標準 API で吸収できないか?」を考え、それでも必要な差分だけをユーティリティに閉じ込める、という順番を意識しておくと健全です。


まとめ:OS判定ユーティリティで身につけるべき感覚

OS判定ユーティリティは、「マルチプラットフォームな Java の中で、どうしても必要な OS 依存を“きれいに隔離する”ための道具」です。

押さえておきたいポイントは次の通りです。

System.getProperty("os.name") をアプリ全体にばらまかず、OsDetector のようなユーティリティに閉じ込める。
OS 種別は enum(OsType)で表現し、isWindows などのメソッド名で意図をはっきりさせる。
パスやコマンドなど、OS ごとに違うものは専用ユーティリティ(LogPathResolverOsCommands)に寄せて、呼び出し側から OS 依存を追い出す。
os.name の判定は「小文字化+部分一致」でゆるく書き、将来の変化にもある程度耐えられるようにする。
そもそも OS で分岐せずに済む標準 API がないか、一度立ち止まって考える癖をつける。

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