条件分岐

【Java】if文の書き方とサンプルコードで学ぶ条件分岐の基本

Javaのif文は条件式の評価結果に応じて処理を分岐できる構文で、if(条件){…}のほか、else ifelseを組み合わせることで複数の分岐が可能です。

条件が真ならif節、偽なら次のelse if、それも偽ならelse節が実行されます。

if文の基本構文

Javaにおいて条件に基づいて処理を分岐させるための最も基本的な構文がif文です。

if文を理解することで、プログラムの流れを柔軟に制御できるようになります。

ここでは、if文の基本的な使い方と、その応用例について詳しく解説します。

ifによる単純分岐

if文は、指定した条件式がtrueと評価された場合にのみ、特定の処理を実行します。

構文は非常にシンプルで、次のように記述します。

if (条件式) {
    // 条件式がtrueの場合に実行される処理
}

この条件式には、比較演算子や論理演算子を用いた式を記述します。

条件式がtrueの場合、波括弧内の処理が実行され、falseの場合はスキップされます。

例えば、変数scoreが60以上かどうかを判定し、「合格です」と表示する例を見てみましょう。

public class App {
    public static void main(String[] args) {
        int score = 80; // 点数を設定
        if (score >= 60) {
            System.out.println("合格です");
        }
    }
}
合格です

このコードでは、scoreが60以上であるため、「合格です」と出力されます。

もしscoreが59だった場合は、何も出力されません。

if -elseによる二分岐

if文だけでは条件が満たされた場合の処理しか書けませんが、条件が満たされなかった場合の処理も必要になることがあります。

そのときはelseを使います。

if (条件式) {
    // 条件式がtrueの場合に実行される処理
} else {
    // 条件式がfalseの場合に実行される処理
}

例として、scoreが60以上なら「合格です」、そうでなければ「再試験です」と表示するプログラムを作成します。

public class App {
    public static void main(String[] args) {
        int score = 50; // 点数を設定
        if (score >= 60) {
            System.out.println("合格です");
        } else {
            System.out.println("再試験です");
        }
    }
}
再試験です

この例では、scoreが50なので、「再試験です」と出力されます。

scoreが70だった場合は、「合格です」と表示されます。

else ifを使った複数分岐

複数の条件を順番に評価したい場合は、else ifを使います。

これにより、複数の条件を段階的に判定し、それぞれに対応した処理を記述できます。

if (条件式1) {
    // 条件式1がtrueの場合の処理
} else if (条件式2) {
    // 条件式2がtrueの場合の処理
} else {
    // どの条件も満たさない場合の処理
}

具体例として、点数に応じて判定を行うプログラムを示します。

public class App {
    public static void main(String[] args) {
        int score = 74; // 点数を設定
        if (score > 90) {
            System.out.println("判定Aです");
        } else if (score > 70) {
            System.out.println("判定Bです");
        } else {
            System.out.println("判定Cです");
        }
    }
}
判定Bです

この例では、scoreが74なので、「判定Bです」と出力されます。

scoreが95の場合は「判定Aです」、65の場合は「判定Cです」となります。

これらの基本的なif文の使い方を理解しておくと、条件に応じた処理を柔軟に記述できるようになります。

次に、より複雑な条件判定やネスト(入れ子)について解説します。

演算子の種類と使い方

Javaにおいて、演算子は値や変数に対してさまざまな操作を行うための記号です。

条件分岐やループ処理など、プログラムの制御構造を構築する上で欠かせない要素です。

ここでは、比較演算子と論理演算子、そして演算子の優先順位と結合について詳しく解説します。

比較演算子(==, !=, >, <, >=, <=)

比較演算子は、二つの値や式を比較し、その結果がtruefalseかを返します。

これらは条件式の中で頻繁に使用され、条件分岐やループの継続判定に役立ちます。

演算子説明結果例
==等しいかどうかa == babが同じ値ならtrue
!=等しくないかa != babが異なる値ならtrue
>より大きいa > babより大きい場合true
<より小さいa < babより小さい場合true
>=以上a >= bab以上の場合true
<=以下a <= bab以下の場合true

例として、変数scoreが60以上かどうかを判定する条件式は次のようになります。

int score = 75;
if (score >= 60) {
    System.out.println("合格です");
}

比較演算子は、数値だけでなく文字列やオブジェクトの比較にも使えますが、文字列の内容比較にはequals()メソッドを使う必要があります。

論理演算子(&&, ||, !)

論理演算子は、複数の条件式を組み合わせて複雑な条件判定を行うために使用します。

演算子説明結果例
&&論理積(AND)条件A && 条件B両方ともtrueならtrue
||論理和(OR)条件A || 条件Bどちらかがtrueならtrue
!論理否定(NOT)!条件条件がfalseならtrue

例として、年齢と身長の両方の条件を満たすかどうかを判定するコードは次のようになります。

int age = 10;
int height = 120;
if (age < 12 && height >= 100) {
    System.out.println("対象です");
}

また、||を使えば、どちらかの条件を満たせば良い場合の判定も可能です。

if (age < 6 || age >= 75) {
    System.out.println("無料です");
}

論理演算子は、条件式の中で複雑な条件を表現する際に非常に便利です。

演算子の優先順位と結合

演算子には優先順位があり、これに従って式の評価順序が決まります。

優先順位が高い演算子は、優先順位が低い演算子よりも先に評価されます。

Javaの演算子の優先順位は次のようになっています(一部抜粋):

  1. 括弧 ()(最も優先度が高く、式の評価順を制御します)
  2. 単項演算子(!++--など)
  3. 比較演算子==!=><>=<=
  4. 論理積 &&
  5. 論理和 ||

また、演算子の結合規則も重要です。

結合規則は、同じ優先順位の演算子が複数並んだ場合に、どちらから評価されるかを決めます。

Javaでは、左から右への左結合が基本です。

例として、次の式を考えます。

boolean result = true || false && false;

この式は、&&の優先順位が||より高いため、まずfalse && falseが評価され、その結果falseとなります。

次にtrue || falseが評価され、最終的にtrueとなります。

括弧を使えば、優先順位を明示的に変更できます。

boolean result = (true || false) && false; // 先に括弧内を評価

この場合は、true || falsetrueとなり、その後true && falseが評価されてfalseとなります。

演算子の優先順位と結合規則を理解しておくと、複雑な条件式も意図通りに評価させることができ、バグの防止やコードの可読性向上につながります。

サンプルコードで学ぶ条件分岐

実際のプログラムでは、条件に応じて異なる処理を行うことが頻繁にあります。

ここでは、具体的なサンプルコードを通じて、条件分岐のさまざまな使い方を理解します。

シンプルなスコア判定から、入れ子のif文、複数条件の組み合わせまで、実践的な例を紹介します。

スコア判定の例

最も基本的な条件分岐の例として、点数に基づく合否判定を考えます。

学生の点数を変数scoreに格納し、その値に応じて結果を出力します。

import java.util.Scanner;

public class App {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("点数を入力してください:");
        int score = scanner.nextInt();
        if (score >= 60) {
            System.out.println("合格です");
        }
        scanner.close(); // リソース解放のためclose()を追加
    }
}
点数を入力してください:75
合格です

この例では、scoreが60以上の場合に「合格です」と表示します。

条件を満たさない場合は何も出力しません。

これを拡張して、合格・不合格の両方を判定させることも可能です。

if (score >= 60) {
    System.out.println("合格です");
} else {
    System.out.println("不合格です");
}

入れ子if文による階層的分岐

複雑な条件判定を行う場合、if文の中にさらにif文を入れることがあります。

これを入れ子のif文と呼びます。

例えば、年齢と性別に応じて料金を決定する例を見てみましょう。

public class App {
    public static void main(String[] args) {
        int age = 20;
        String gender = "男性";
        String fee;
        if (age < 6) {
            fee = "無料";
        } else {
            if (age <= 12) {
                fee = "300円";
            } else {
                if (gender.equals("女性")) {
                    fee = "500円";
                } else {
                    fee = "700円";
                }
            }
        }
        System.out.println("入園料は" + fee + "です。");
    }
}
入園料は700円です。

この例では、年齢が6歳未満なら無料、それ以降は年齢や性別に応じて料金を設定しています。

入れ子のif文を使うことで、階層的な条件判定が可能です。

複数条件を組み合わせた実践例

実務では、複数の条件を同時に満たす必要があるケースも多いです。

&&||を使って条件を組み合わせることで、柔軟な判定が行えます。

例として、学生の成績と出席状況に基づき、最終的な評価を決めるプログラムを作成します。

public class App {
    public static void main(String[] args) {
        int score = 85;
        int attendance = 90; // 出席率(%)
        if (score >= 80 && attendance >= 75) {
            System.out.println("優秀な成績です");
        } else if (score >= 60 && attendance >= 50) {
            System.out.println("普通の成績です");
        } else {
            System.out.println("改善が必要です");
        }
    }
}
優秀な成績です

この例では、点数と出席率の両方の条件を満たすかどうかで評価を分けています。

複数条件を組み合わせることで、より実践的な判定が可能となります。

これらのサンプルコードを通じて、条件分岐の基本的な使い方から応用までを理解し、実際のプログラムに役立ててください。

条件分岐は、プログラムの流れを自在にコントロールするための重要な技術です。

文字列・コレクションの判定方法

Javaにおいて、文字列やコレクションの内容を比較する際には、適切な方法を選ぶことが重要です。

誤った比較方法を用いると、意図しない結果を招くことがあります。

ここでは、文字列の比較におけるequals()==の違い、そしてリストやマップの要素チェックの方法について詳しく解説します。

文字列比較:equalsと==の違い

Javaでは、文字列を比較する際に==演算子とequals()メソッドの両方を使うことができますが、その動作は異なります。

  • ==演算子は、二つの文字列オブジェクトの参照(メモリ上のアドレス)を比較します。つまり、同じ文字列リテラルや同じインスタンスを指している場合にtrueを返します
  • equals()メソッドは、文字列の内容(文字列の中身)を比較します。内容が同じであればtrueを返します

例を見てみましょう。

public class App {
    public static void main(String[] args) {
        String str1 = "こんにちは"; // 文字列リテラル
        String str2 = "こんにちは"; // 文字列リテラル
        String str3 = new String("こんにちは"); // newインスタンス
        System.out.println(str1 == str2); // true(文字列リテラルは同じインスタンスを指すことが多い)
        System.out.println(str1 == str3); // false(異なるインスタンス)
        System.out.println(str1.equals(str3)); // true(内容は同じ)
    }
}
true
false
true

この例では、str1str2は文字列リテラルのため、同じインスタンスを指していることが多く、==trueを返します。

一方、str3newキーワードで作成された新しいインスタンスなので、==falseですが、equals()は内容を比較してtrueを返します。

ポイント:文字列の内容を比較したい場合は、equals()を使うことが推奨されます。

==は参照の比較にしか使えません。

リストやマップの要素チェック

コレクション(リストやマップ)の要素を判定する場合、内容の一致を確認するためにcontains()メソッドやequals()メソッドを使います。

リストの要素チェック

ArrayListLinkedListなどのリストでは、contains()メソッドを使うと、指定した要素がリスト内に存在するかどうかを判定できます。

import java.util.ArrayList;

public class App {
    public static void main(String[] args) {
        ArrayList<String> fruits = new ArrayList<>();
        fruits.add("リンゴ");
        fruits.add("バナナ");
        fruits.add("オレンジ");
        boolean hasBanana = fruits.contains("バナナ");
        System.out.println("バナナはリストに含まれていますか? " + hasBanana);
    }
}
バナナはリストに含まれていますか? true

この例では、「バナナ」がリストに含まれているかどうかをcontains()で判定しています。

contains()は、内部でequals()を使って要素の一致を確認します。

マップの要素チェック

HashMapTreeMapでは、キーや値の存在をcontainsKey()containsValue()で判定します。

import java.util.HashMap;
public class App {
    public static void main(String[] args) {
        HashMap<Integer, String> studentMap = new HashMap<>();
        studentMap.put(1, "佐藤");
        studentMap.put(2, "鈴木");
        studentMap.put(3, "高橋");
        boolean hasStudent2 = studentMap.containsKey(2);
        boolean hasNameSato = studentMap.containsValue("佐藤");
        System.out.println("ID 2の学生はいますか? " + hasStudent2);
        System.out.println("名前が佐藤の学生はいますか? " + hasNameSato);
    }
}
ID 2の学生はいますか? true
名前が佐藤の学生はいますか? true

このように、コレクションの中に特定の要素やキーが存在するかどうかを判定するには、それぞれのcontains系メソッドを使います。

これらの比較方法や要素チェックのテクニックを理解しておくと、コレクションや文字列の内容を正確に判定でき、バグの少ない堅牢なプログラムを作成できます。

三項演算子で書く簡潔な分岐

三項演算子は、条件式の評価結果に応じて値を選択するための短縮記法です。

if-else文を使うよりもコードをコンパクトに書くことができ、シンプルな条件判定に適しています。

ここでは、基本的な構文と書き方、そしてネストした三項演算子を使う際の注意点について解説します。

基本構文と書き方

三項演算子は、次のような構文で記述します。

条件式 ? 真の場合の値 : 偽の場合の値;
  • 条件式には、真偽値を返す式を記述します
  • ?の後に続くのが、条件式がtrueの場合に返す値です
  • :の後に続くのが、条件式がfalseの場合に返す値です

この構文は、if-else文の代わりに使うことができ、値の代入や返り値の設定に便利です。

例:数値の大小比較

public class App {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int max = (a > b) ? a : b;
        System.out.println("大きい値は " + max + " です。");
    }
}
大きい値は 20 です。

この例では、a > bの条件がtrueならaを、そうでなければbmaxに代入します。

結果は20となり、「大きい値は 20 です。」と出力されます。

例:文字列の判定

public class App {
    public static void main(String[] args) {
        int score = 75; // 例として75点とします

        // 三項演算子を使って合格・不合格を判定
        String result = (score >= 60) ? "合格" : "不合格";

        // 結果を出力
        System.out.println("試験結果: " + result);
    }
}
試験結果: 合格

このように、三項演算子は値の選択に非常に便利です。

ネストした三項演算子の注意点

複雑な条件判定を行う場合、三項演算子をネストして使うこともあります。

ただし、ネストはコードの可読性を損なう可能性があるため、注意が必要です。

ネストの例

public class App {
    public static void main(String[] args) {
        int score = 85;
        String grade = (score >= 90) ? "A" :
                       (score >= 80) ? "B" :
                       (score >= 70) ? "C" : "F";
        System.out.println("成績は " + grade + " です。");
    }
}
成績は B です。

この例では、scoreの値に応じてABCFを選択しています。

条件が多くなると、ネストの深さも増し、読みづらくなるため、複雑な条件判定にはif-else文の方が適している場合もあります。

注意点

  • 可読性の確保:ネストが深くなると、条件の理解が難しくなるため、必要最低限に留める
  • 複雑な条件には不向き:複数の条件を組み合わせる場合や、複雑なロジックにはif-else文やメソッド分割を検討します
  • 演算子の優先順位に注意:ネストした場合でも、演算子の優先順位を理解し、必要に応じて括弧を使って明示的に評価順序を制御します

三項演算子は、シンプルな条件分岐を短く書きたいときに非常に便利です。

適切に使いこなすことで、コードの見通しを良くし、効率的なプログラミングが可能となります。

switch文との使い分け

switch文は、複数の条件に基づいて処理を分岐させる際に便利な構文です。

if文と比較して、特定の値に対する分岐を簡潔に記述できるため、コードの見通しやすさや可読性が向上します。

ただし、使いどころを誤ると逆に複雑さを増すこともあるため、適切な使い分けが重要です。

ここでは、switch文の基本構文、Java 14以降のswitch式の活用例、そしてif文とswitch文の選択基準について解説します。

switch文の基本構文

switch文は、指定した変数や式の値に応じて処理を分岐させるための構文です。

基本的な構文は次の通りです。

switch () {
    case1:
        // 値1に一致した場合の処理
        break;
    case2:
        // 値2に一致した場合の処理
        break;
    default:
        //どのcaseにも一致しない場合の処理
}
  • には、byteshortintcharStringenum型の値を指定できます
  • caseには比較対象となる値を記述します
  • breakは、switch文の実行を終了させるために必要です。これがないと、次のcaseに処理が流れてしまいます
  • defaultは、どのcaseにも一致しなかった場合に実行される処理です

例:曜日に応じた処理

public class App {
    public static void main(String[] args) {
        String day = "火曜日"; // 変数の定義
        switch (day) {
            case "月曜日":
                System.out.println("週の始まりです");
                break;
            case "火曜日":
                System.out.println("火曜日です");
                break;
            default:
                System.out.println("その他の日です");
        }
    }
}
火曜日です

この例では、day"火曜日"の場合、「火曜日です」と出力します。

switch式(Java 14以降)の活用

Java 14から導入されたswitch式は、従来のswitch文をより簡潔に書くことができる新しい構文です。

switch式は値を返すことができ、変数への代入やメソッドの引数として直接使えます。

public class App {
    public static void main(String[] args) {
        int score = 85;
        String grade = switch (score / 10) {
            case 10, 9:
                yield "A";
            case 8:
                yield "B";
            case 7:
                yield "C";
            default:
                yield "F";
        };
        System.out.println("成績は " + grade);
    }
}
成績は B

この例では、switch式の中でyieldキーワードを使って値を返し、gradeに代入しています。

switch式は、複数のcaseをカンマで区切ってまとめたり、breakの代わりにyieldを使ったりと、従来のswitch文よりも表現力が向上しています。

if文とswitch文の選択基準

if文とswitch文は、どちらも条件分岐に使えますが、使い分けるポイントがあります。

  • if文が適している場合
    • 条件が範囲や複雑な論理式を含む場合(例:score >= 60 && age < 20)
    • 比較対象が文字列やオブジェクトの内容比較の場合(equals()を使う必要がある)
    • 条件が真偽値や複雑な条件式のとき
    • 複数の条件を論理演算子で組み合わせる必要がある場合
  • switch文が適している場合
    • 比較対象が定数値intcharStringenumで、値の種類が限定されている場合
    • 複数の値に対して同じ処理を行いたいとき(caseをまとめる)
    • 条件が単純な値の一致判定だけの場合

まとめると、条件が単純な値の一致判定であればswitch文を使い、範囲や複雑な論理式を含む場合はif文を選ぶのが良いです。

switch式は、値の返しやすさやコードの簡潔さを重視したいときに有効です。

これらのポイントを押さえて、状況に応じて適切な条件分岐構文を選択しましょう。

null安全な条件分岐

Javaでは、null参照に対して適切に対処しないと、NullPointerExceptionが発生しやすくなります。

特に、オブジェクトのメソッドやフィールドにアクセスする前に、そのオブジェクトがnullでないかどうかを確認することが重要です。

ここでは、nullチェックの一般的な方法と、Java 8以降で導入されたOptionalクラスを使ったnull安全な書き方について解説します。

nullチェックの一般的な方法

nullチェックは、条件式を使って行います。

最も基本的な方法は、if文を用いて対象のオブジェクトがnullかどうかを判定し、その後の処理を制御することです。

public class App {
    public static void main(String[] args) {
        String name = getName();
        if (name != null) {
            System.out.println("名前は " + name + " です。");
        } else {
            System.out.println("名前が設定されていません。");
        }
    }
    // getNameメソッドの仮実装
    public static String getName() {
        // 実際の実装に応じて変更してください
        return "山田太郎"; // 仮の戻り値
    }
}
名前は 山田太郎 です。

この例では、namenullでない場合にのみ、その内容を出力します。

nullの場合は別の処理を行うことができます。

ただし、複数の場所でnullチェックを繰り返すと、コードが冗長になりやすいため、メソッド化やユーティリティクラスを使って共通化することも検討します。

また、Java 7以降では、ObjectsクラスのrequireNonNull()メソッドを使って、引数のnullチェックを簡潔に行うことも可能です。

import java.util.Objects;

public class App {
    private String name;

    public void setName(String name) {
        this.name = Objects.requireNonNull(name, "名前はnullにできません");
    }

    public static void main(String[] args) {
        App app = new App();
        // 例としてsetNameのテスト
        app.setName("Test Name");
        System.out.println("Name set to: " + app.name);
    }
}
Name set to: Test Name

この方法は、引数がnullだった場合に例外をスローし、早期にエラーを検知できるメリットがあります。

Optionalを使ったnull安全な書き方

Java 8から導入されたOptionalクラスは、null値を安全に扱うための便利な仕組みです。

Optionalは、値が存在するかどうかを明示的に表現し、nullに起因するエラーを防ぎます。

Optionalの基本的な使い方

import java.util.Optional;

public class App {
    public static void main(String[] args) {
        Optional<String> nameOpt = getNameOptional();
        // 値が存在する場合のみ処理
        nameOpt.ifPresent(name -> System.out.println("名前は " + name + " です。"));
        // 値が存在しない場合のデフォルト値を設定
        String name = nameOpt.orElse("未設定");
        System.out.println("名前は " + name + " です。");
    }

    public static Optional<String> getNameOptional() {
        String name = getName(); // 何らかの方法で名前を取得
        return Optional.ofNullable(name);
    }

    public static String getName() {
        // 名前が取得できない場合は null を返す
        return null; // ここでは null を返す例
    }
}
名前は 未設定 です。

この例では、getNameOptional()メソッドがOptional<String>を返し、呼び出し側はifPresent()orElse()を使って安全に値を取り出します。

Optionalのメリット

  • nullを直接扱わず、値の有無を明示的に管理できる
  • nullによるNullPointerExceptionのリスクを低減できる
  • メソッドチェーンで処理を連結でき、コードの見通しが良くなる

注意点

  • Optionalは、あくまで値の有無を表現するためのものであり、フィールドやメソッドの戻り値に使うことが推奨される
  • パフォーマンスに影響を与える場合もあるため、多用しすぎに注意

null安全な条件分岐を実現するためには、これらの方法を適切に使い分けることが重要です。

nullチェックは基本的な操作ですが、Optionalを活用することで、より堅牢で読みやすいコードを作ることが可能です。

可読性と保守性を高めるコツ

コードの可読性と保守性を向上させることは、長期的に見てソフトウェアの品質を保つために非常に重要です。

複雑な条件分岐や長いメソッドは、理解や修正を難しくし、バグの原因となることがあります。

ここでは、ネストを浅く保つ工夫と、メソッド抽出による分岐の整理方法について解説します。

ネストを浅く保つテクニック

深いネストは、コードの見通しを悪くし、理解や修正を困難にします。

ネストを浅く保つための基本的なテクニックは以下の通りです。

  • 早期リターンを使う

条件に合わない場合は早めにメソッドから抜けることで、ネストの深さを減らすことができます。

public void processOrder(Order order) {
    if (order == null) {
        return; // nullの場合は何もしない
    }
    if (!order.isPaid()) {
        return; // 支払い済みでなければ処理しない
    }
    // 以降の処理は、nullや未支払いのケースを除外した後に書く
    // これによりネストが浅くなる
    shipOrder(order);
}
  • 条件を否定形にしない

条件をポジティブに保つことで、条件式の理解が容易になります。

// 悪い例
if (!user.isActive()) {
    // 処理
}
// 良い例
if (user.isActive()) {
    // 処理
}
  • ガード節を活用する

複数の条件を早期に排除し、主要な処理をインデントの深さを増やさずに書きます。

public void handleRequest(Request request) {
    if (request == null || !request.isValid()) {
        return;
    }
    // 主要な処理はここに書く
}

メソッド抽出による分岐の整理

複雑な条件分岐は、メソッドに分割して整理することで、コードの見通しやすさと再利用性を高めることができます。

  • 条件ごとにメソッドを作る

条件判定や処理を個別のメソッドに分離し、呼び出す側はシンプルに保ちます。

public void processOrder(Order order) {
    if (isNullOrder(order) || isUnpaidOrder(order)) {
        return;
    }
    shipOrder(order);
}
private boolean isNullOrder(Order order) {
    return order == null;
}
private boolean isUnpaidOrder(Order order) {
    return !order.isPaid();
}
  • 複雑な条件判定をメソッドにまとめる

条件式をメソッドに抽出することで、条件の意味が明確になり、コードの意図が伝わりやすくなります。

if (isEligibleForDiscount(customer)) {
    applyDiscount(customer);
}
private boolean isEligibleForDiscount(Customer customer) {
    return customer.getAge() > 65 || customer.hasMembership();
}
  • 状態やロジックをメソッドに分離

条件判定だけでなく、処理の一部もメソッドに分割し、責務を明確にします。

public void processPayment(Payment payment) {
    if (isValidPayment(payment)) {
        finalizePayment(payment);
    }
}
private boolean isValidPayment(Payment payment) {
    return payment != null && payment.getAmount() > 0;
}

これらのテクニックを駆使することで、条件分岐の複雑さを抑え、コードの可読性と保守性を大きく向上させることができます。

長期的な視点でコードの整理を心がけることが、良いソフトウェア開発の基本です。

テストで検証するif文

条件分岐を含むコードの正確性を確保するためには、単体テストを実施することが不可欠です。

特に、if文を含むロジックは、さまざまな条件下で正しく動作するかどうかを検証する必要があります。

ここでは、Javaの代表的なテストフレームワークであるJUnitを使った単体テストの例と、Mockitoを用いて条件をモックし、複雑な条件分岐を効率的にテストする方法について解説します。

JUnitを使った単体テスト例

JUnitは、Javaの標準的な単体テストフレームワークです。

if文を含むメソッドの動作を検証するために、さまざまな入力値に対して期待される出力や状態を確認します。

例:年齢に応じた割引判定のテスト

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;

public class Customer {
    private int age;

    public Customer(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}

public class App {

    public static void main(String[] args) {
        int age = 60; // ここを変更して任意の年齢で動作確認可能
        Customer customer = new Customer(age);
        boolean eligible = isEligibleForSeniorDiscount(customer);
        if (eligible) {
            System.out.println(age + "歳はシニア割引対象です。");
        } else {
            System.out.println(age + "歳はシニア割引対象外です。");
        }
    }

    public static boolean isEligibleForSeniorDiscount(Customer customer) {
        return customer.getAge() >= 65;
    }

    @Test
    public void testSeniorDiscount() {
        Customer customer = new Customer(70);
        boolean isEligible = isEligibleForSeniorDiscount(customer);
        assertTrue(isEligible, "70歳はシニア割引対象のはず");
    }

    @Test
    public void testNonSenior() {
        Customer customer = new Customer(50);
        boolean isEligible = isEligibleForSeniorDiscount(customer);
        assertFalse(isEligible, "50歳はシニア割引対象外のはず");
    }
}
60歳はシニア割引対象外です。

この例では、Customerクラスの年齢に基づいて割引対象かどうかを判定するif文の動作を、JUnitのassertTrueassertFalseを使って検証しています。

さまざまな入力値を用意し、期待される結果と比較することで、ロジックの正確性を担保します。

Mockitoで条件をモックする方法

Mockitoは、依存関係のあるオブジェクトをモック(模擬)し、特定の条件や状態を再現するためのフレームワークです。

複雑な条件分岐や外部システムとの連携を伴うロジックのテストに有効です。

例:ユーザの状態に応じた処理のテスト

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
public class UserServiceTest {
    @Test
    public void testUserActive() {
        UserRepository userRepository = mock(UserRepository.class);
        User user = mock(User.class);
        when(userRepository.findUserById(1)).thenReturn(user);
        when(user.isActive()).thenReturn(true);
        UserService userService = new UserService(userRepository);
        boolean result = userService.processUser(1);
        assertTrue(result, "アクティブなユーザは処理成功");
    }
    @Test
    public void testUserInactive() {
        UserRepository userRepository = mock(UserRepository.class);
        User user = mock(User.class);
        when(userRepository.findUserById(2)).thenReturn(user);
        when(user.isActive()).thenReturn(false);
        UserService userService = new UserService(userRepository);
        boolean result = userService.processUser(2);
        assertFalse(result, "非アクティブなユーザは処理失敗");
    }
}

この例では、UserRepositoryUserの依存関係をモック化し、isActive()の返り値を制御しています。

これにより、実際のデータベースや外部システムに依存せずに、if文の条件分岐の動作を検証できます。

これらのテスト手法を活用することで、if文を含むロジックの動作保証と品質向上を図ることが可能です。

JUnitによる基本的な検証と、Mockitoを使った条件のモックを組み合わせることで、さまざまなケースに対応した堅牢なテストを実現できます。

よくあるミスとトラブルシューティング

条件分岐を含むプログラムを書く際には、いくつかの典型的なミスや落とし穴に注意が必要です。

これらを理解し、適切に対処することで、バグの発生を未然に防ぎ、安定した動作を実現できます。

ここでは、波括弧の省略による誤動作、条件式の誤記とそのデバッグ方法、そしてNullPointerExceptionの回避策について詳しく解説します。

波括弧省略による落とし穴

Javaでは、ifelseの後に波括弧{}を省略して単一の文だけを書き、その後に続く文を条件に関係なく実行させることが可能です。

しかし、この書き方は誤解やミスを招きやすく、バグの原因となることがあります。

if (score >= 60)
    System.out.println("合格");
    System.out.println("お疲れ様でした");

この例では、ifの条件に続く最初のSystem.out.println("合格");だけが条件付きで実行されます。

次のSystem.out.println("お疲れ様でした");は、ifの条件に関係なく常に実行されてしまいます。

  • 常に波括弧を使う習慣をつける
  • コードの可読性と安全性を高めるために、波括弧を省略しない
if (score >= 60) {
    System.out.println("合格");
}
System.out.println("お疲れ様でした");

条件式の誤記とデバッグ

条件式の誤記は、意図しない動作やバグの原因となります。

特に、比較演算子の誤用や論理演算子の優先順位の理解不足が多く見られます。

よくある誤り例

  • ===の混同:

=は代入演算子、==は比較演算子です。

if (a = b)は、abを代入し、その結果を条件として評価してしまうため、意図しない動作になります。

  • 論理演算子の優先順位の誤解:

&&||の優先順位を理解せずに複雑な条件を書き、意図しない結果になることがあります。

if (a > 0 || b > 0 && c > 0) {
    // 期待通りに動作しない可能性
}

デバッグ方法:

  • 条件式を段階的に分解し、個別に評価してみる
  • System.out.println()を使って、各条件の結果を出力し、どの条件が真または偽かを確認します
  • IDEのデバッガを使い、ブレークポイントを設定して条件式の評価過程を追います

NullPointerExceptionの回避策

NullPointerExceptionは、null参照に対してメソッド呼び出しやフィールドアクセスを行ったときに発生します。

条件分岐の中で頻繁に見られるエラーであり、事前のnullチェックや適切な設計が必要です。

回避策

  • nullチェックを徹底します:

条件式の前にnullかどうかを確認し、nullの場合は別の処理を行います。

if (user != null && user.isActive()) {
    // userがnullでなければisActive()を呼び出す
}
  • Objects.requireNonNull()を使います:

引数や変数にnullが入ることを許さない場合に、早期に例外をスローして検知。

import java.util.Objects;
public void setUser(User user) {
    this.user = Objects.requireNonNull(user, "User cannot be null");
}
  • Optionalを活用します:

Optionalを使えば、nullの可能性を明示的に扱い、安全に値を取り出せます。

Optional<User> userOpt = Optional.ofNullable(user);
userOpt.ifPresent(u -> u.isActive());
  • 設計段階でnullを避けます:

必要に応じて、nullを返さずに空のオブジェクトやデフォルト値を返す設計にします。

これらのトラブルシューティングのポイントを押さえることで、条件分岐に関するバグや例外の発生を未然に防ぎ、安定したプログラムを作ることができます。

常にコードの書き方や設計に注意を払い、ミスを最小限に抑える工夫を心がけましょう。

まとめ

この記事では、Javaの条件分岐に関する基本的な書き方や応用例、そしてよくあるミスやトラブルの対処法について解説しました。

if文とswitch文の使い分けや、null安全なコーディングのポイント、テスト方法も紹介しています。

これらを理解し適切に活用することで、バグの少ない堅牢なプログラムを作成できるようになります。

関連記事

Back to top button
目次へ