Java Tips | 実務ユーティリティ:範囲チェック

Java Java
スポンサーリンク
スポンサーリンク

なぜ「範囲チェックユーティリティ」が業務で重要になるのか

業務システムでは、数値・日付・文字列長などに「許される範囲」が必ず存在します。 例えば次のような制約です。

  • ページサイズは 1〜100 の間
  • 割引率は 0〜100(%)の間
  • 年齢は 0〜120 の間
  • パスワードの文字数は 8〜64 文字の間
  • 日付は「今日以降」「過去 1 年以内」など

こうした範囲を守らない値が入ってくると、 計算結果が壊れたり、DB制約違反になったり、セキュリティ上の問題が発生したりします。

毎回 if 文で範囲チェックを書いていると、

if (value < 0 || value > 100) {
    throw new IllegalArgumentException("範囲外です");
}
Java

といったコードがあちこちに散らばり、 「どこで何の範囲をチェックしているのか」が分かりづらくなります。

そこで役に立つのが 範囲チェックユーティリティ です。 「この値はこの範囲に入っているか」を共通の関数で判定できるようにすることで、 業務ロジックの読みやすさ・保守性・安全性が大きく向上します。

ここから、プログラミング初心者向けにステップバイステップで、 実務で使える範囲チェックユーティリティを丁寧に解説していきます。

範囲チェックの基本発想を理解する

「許される最小値」と「許される最大値」を明確にする

まず、範囲チェックの基本はとてもシンプルです。

  • 最小値(min)
  • 最大値(max)

この 2 つを決めて、 「値が min 以上かつ max 以下かどうか」 を判定します。

例えば、割引率(%)の範囲チェックは次のようになります。

int discount = 50; // 入力値

if (discount < 0 || discount > 100) {
    throw new IllegalArgumentException("割引率は 0〜100 の間で指定してください");
}
Java

この「min〜max の間かどうか」を、 毎回 if 文で書くのではなく、ユーティリティとしてまとめるのが範囲チェックユーティリティの役割です。

ステップ1 最も基本的な数値範囲チェックユーティリティを書く

「min ≤ value ≤ max」を関数にする

まずは、整数の範囲チェックを行う基本的なユーティリティを作ります。

public class Range {

    /**
     * value が min 以上 max 以下なら true を返します。
     */
    public static boolean between(int value, int min, int max) {
        return value >= min && value <= max;
    }
}
Java

使い方の例です。

public class RangeBasicExample {

    public static void main(String[] args) {
        int discount = 50;

        if (!Range.between(discount, 0, 100)) {
            throw new IllegalArgumentException("割引率は 0〜100 の間で指定してください");
        }

        System.out.println("割引率は範囲内です");
    }
}
Java

ここで重要なのは、 「範囲チェックの条件式をユーティリティに閉じ込める」 という発想です。 これにより、呼び出し側のコードは「何をしたいか」に集中でき、 「どう判定するか」はユーティリティに任せられます。

ステップ2 境界を含むか含まないかを意識する

「以上/以下」と「より大きい/より小さい」の違いを明確にします

範囲チェックでは、次の 2 つの違いが重要です。

  • 閉区間(min ≤ value ≤ max)
    • 境界値を含む
    • 例:年齢は 0〜120 歳まで許可
  • 開区間(min < value < max)
    • 境界値を含まない
    • 例:0 より大きく 1 より小さい(0〜1 の間の小数)

これをユーティリティとして分けておくと、 業務ロジックの意図がとても読みやすくなります。

public class Range {

    // 閉区間:min ≤ value ≤ max
    public static boolean closed(int value, int min, int max) {
        return value >= min && value <= max;
    }

    // 開区間:min < value < max
    public static boolean open(int value, int min, int max) {
        return value > min && value < max;
    }
}
Java

使い方の例です。

int age = 20;
if (!Range.closed(age, 0, 120)) {
    throw new IllegalArgumentException("年齢は 0〜120 の間で指定してください");
}

double ratio = 0.5;
if (!Range.open((int)(ratio * 10), 0, 10)) {
    // ここは例として整数に変換していますが、後で小数版を作ります
}
Java

ここで深掘りしたいポイントは、 「境界を含むかどうかは仕様としてとても重要」 ということです。 ユーティリティ名でそれを表現しておくと、コードを読む人が迷いません。

ステップ3 ジェネリック+Comparableで汎用的な範囲チェックを作る

数値だけでなく日付や文字列にも使える形にする

Java では、Comparable<T> を実装している型(Integer, Long, BigDecimal, LocalDate, String など)であれば、 compareTo を使って大小比較ができます。

これを利用して、汎用的な範囲チェックユーティリティを作ります。

public class Range {

    /**
     * 汎用的な閉区間範囲チェック:min ≤ value ≤ max
     */
    public static <T extends Comparable<T>> boolean closed(T value, T min, T max) {
        if (value == null || min == null || max == null) {
            throw new IllegalArgumentException("value, min, max は null であってはなりません");
        }
        return value.compareTo(min) >= 0 && value.compareTo(max) <= 0;
    }

    /**
     * 汎用的な開区間範囲チェック:min < value < max
     */
    public static <T extends Comparable<T>> boolean open(T value, T min, T max) {
        if (value == null || min == null || max == null) {
            throw new IllegalArgumentException("value, min, max は null であってはなりません");
        }
        return value.compareTo(min) > 0 && value.compareTo(max) < 0;
    }
}
Java

使い方の例です。

import java.time.LocalDate;

public class RangeGenericExample {

    public static void main(String[] args) {
        // 整数
        Integer age = 30;
        boolean ageOk = Range.closed(age, 0, 120);

        // 日付:今日から 30 日後まで
        LocalDate today = LocalDate.now();
        LocalDate target = today.plusDays(10);
        boolean dateOk = Range.closed(target, today, today.plusDays(30));

        // 文字列:辞書順で "A"〜"Z" の間かどうか
        String code = "M";
        boolean codeOk = Range.closed(code, "A", "Z");

        System.out.println("ageOk = " + ageOk);
        System.out.println("dateOk = " + dateOk);
        System.out.println("codeOk = " + codeOk);
    }
}
Java

ここで重要なのは、 「Comparable を使うことで、数値だけでなく日付や文字列にも同じ範囲チェックロジックを適用できる」 という点です。 これにより、範囲チェックユーティリティがプロジェクト全体で再利用しやすくなります。

ステップ4 範囲外のときに例外を投げるユーティリティを作る

「チェック+例外」をセットにしておくと呼び出し側がスッキリします

範囲チェックは、「true/false を返す」だけでなく、 範囲外のときに例外を投げたい場面も多くあります。

例えば、APIの入力値が範囲外だったら 400 エラーにしたい、 業務ロジックの中で範囲外なら即座に処理を止めたい、などです。

そのためのユーティリティを作ります。

java

public class RangeAssert {

    /**
     * value が min〜max の閉区間に含まれない場合、IllegalArgumentException を投げます。
     */
    public static void assertClosed(int value, int min, int max, String label) {
        if (!Range.closed(value, min, max)) {
            throw new IllegalArgumentException(
                    label + " は " + min + "〜" + max + " の範囲で指定してください(指定値: " + value + ")");
        }
    }

    /**
     * 汎用版:Comparable を使った閉区間チェック+例外。
     */
    public static <T extends Comparable<T>> void assertClosed(T value, T min, T max, String label) {
        if (!Range.closed(value, min, max)) {
            throw new IllegalArgumentException(
                    label + " は " + min + "〜" + max + " の範囲で指定してください(指定値: " + value + ")");
        }
    }
}
Java

使い方の例です。

public class RangeAssertExample {

    public static void main(String[] args) {
        int discount = 150;

        RangeAssert.assertClosed(discount, 0, 100, "割引率");
        // ここで IllegalArgumentException が投げられます
    }
}
Java

ここで深掘りしたいポイントは、 「範囲チェックとエラーメッセージ生成をユーティリティにまとめておく」 ことで、呼び出し側のコードが非常に読みやすくなるということです。

ステップ5 文字列長の範囲チェックユーティリティを作る

パスワード・コメント・コードなどでよく使います

業務システムでは、「文字数の範囲」をチェックする場面が非常に多いです。

  • パスワードは 8〜64 文字
  • コメントは 0〜500 文字
  • コードは 1〜10 文字

これをユーティリティとしてまとめます。

public class LengthRange {

    /**
     * 文字列の長さが min〜max の閉区間に含まれるかどうかを判定します。
     * null の場合は長さ 0 とみなします。
     */
    public static boolean closed(String value, int min, int max) {
        int len = (value == null) ? 0 : value.length();
        return len >= min && len <= max;
    }

    /**
     * 範囲外の場合に例外を投げる版。
     */
    public static void assertClosed(String value, int min, int max, String label) {
        int len = (value == null) ? 0 : value.length();
        if (!closed(value, min, max)) {
            throw new IllegalArgumentException(
                    label + " の長さは " + min + "〜" + max + " 文字で指定してください(現在: " + len + " 文字)");
        }
    }
}
Java

使い方の例です。

public class LengthRangeExample {

    public static void main(String[] args) {
        String password = "abc123";

        LengthRange.assertClosed(password, 8, 64, "パスワード");
        // ここで IllegalArgumentException が投げられます
    }
}
Java

ここで重要なのは、 「文字列長の範囲チェックも、数値の範囲チェックと同じ発想でユーティリティ化できる」 という点です。 これにより、バリデーションロジックを一箇所に集約できます。

ステップ6 セキュリティ視点での範囲チェックの重要性

「過剰な値」「極端な値」を防ぐことが攻撃対策になります

セキュリティスペシャリストの視点から見ると、 範囲チェックは次のような意味を持ちます。

  • 過剰な値を防ぐ
    • 例:ページサイズに 1,000,000 を指定して大量データを取得しようとする攻撃
    • 例:文字列長に極端に長い値を入れてバッファを圧迫する攻撃
  • 極端な値を防ぐ
    • 例:負の値や異常に大きな値を入れて計算結果を壊す攻撃
    • 例:日付にありえない範囲(西暦 0001 年など)を入れて処理を壊す攻撃
  • 業務仕様を守る
    • 範囲チェックは、単なる技術的な制約ではなく、 「業務として許される値の範囲」を守るための仕組みでもあります。

範囲チェックユーティリティを通じて、 「この項目はこの範囲にしかならない」 という前提をコードとして明文化しておくことで、 攻撃や誤入力による異常な状態を防ぎやすくなります。

ステップ7 実務で使いやすい範囲チェックテンプレートとしてまとめる

数値・日付・文字列長をひとまとめにする

ここまでの考え方をまとめて、 プロジェクト全体で使い回せる範囲チェックテンプレートとして整理しておきます。

public class RangeUtils {

    // 数値:閉区間
    public static boolean between(int value, int min, int max) {
        return value >= min && value <= max;
    }

    public static void assertBetween(int value, int min, int max, String label) {
        if (!between(value, min, max)) {
            throw new IllegalArgumentException(
                    label + " は " + min + "〜" + max + " の範囲で指定してください(指定値: " + value + ")");
        }
    }

    // Comparable 汎用版:閉区間
    public static <T extends Comparable<T>> boolean between(T value, T min, T max) {
        if (value == null || min == null || max == null) {
            throw new IllegalArgumentException("value, min, max は null であってはなりません");
        }
        return value.compareTo(min) >= 0 && value.compareTo(max) <= 0;
    }

    public static <T extends Comparable<T>> void assertBetween(T value, T min, T max, String label) {
        if (!between(value, min, max)) {
            throw new IllegalArgumentException(
                    label + " は " + min + "〜" + max + " の範囲で指定してください(指定値: " + value + ")");
        }
    }

    // 文字列長:閉区間
    public static boolean lengthBetween(String value, int min, int max) {
        int len = (value == null) ? 0 : value.length();
        return len >= min && len <= max;
    }

    public static void assertLengthBetween(String value, int min, int max, String label) {
        int len = (value == null) ? 0 : value.length();
        if (!lengthBetween(value, min, max)) {
            throw new IllegalArgumentException(
                    label + " の長さは " + min + "〜" + max + " 文字で指定してください(現在: " + len + " 文字)");
        }
    }
}
Java

呼び出し側のイメージです。

// 割引率
RangeUtils.assertBetween(discount, 0, 100, "割引率");

// 年齢
RangeUtils.assertBetween(age, 0, 120, "年齢");

// 日付
RangeUtils.assertBetween(targetDate, startDate, endDate, "対象日付");

// パスワード長
RangeUtils.assertLengthBetween(password, 8, 64, "パスワード");
Java

このようにしておくことで、 「範囲チェックのルール」を一箇所に集約でき、 後から方針を変えたいときもユーティリティだけを修正すれば済むようになります。

まとめ 範囲チェックユーティリティで身につけてほしい感覚

範囲チェックユーティリティは、業務システムの「入力の健全性」「業務仕様の遵守」「セキュリティ」を支える重要な仕組みです。 そこには次のようなポイントがあります。

  • 範囲チェックは「min〜max の間かどうか」を判定するシンプルなロジックですが、あちこちに散らばると保守が難しくなることです。
  • 境界を含むかどうか(閉区間/開区間)をユーティリティ名で表現しておくことで、業務仕様の意図がコードから読み取りやすくなることです。
  • Comparable を使った汎用的な範囲チェックにより、数値だけでなく日付や文字列にも同じロジックを適用できることです。
  • 文字列長の範囲チェックをユーティリティ化することで、パスワード・コメント・コードなどのバリデーションを一箇所に集約できることです。
  • セキュリティ的にも、過剰な値・極端な値を防ぐことで、攻撃や誤入力による異常な状態を防ぎやすくなることです。

こうした感覚を範囲チェックユーティリティとして形にしておくことで、 どの機能からも同じルールで「許される範囲」を扱えるようになり、 システム全体の信頼性と運用しやすさが自然に底上げされていきます。

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