本ページはプロモーションが含まれています

Java入門

Javaの三項演算子とは?使い方とif文との違いを解説

トム

・都内自社開発IT企業勤務/javaのバックエンドエンジニア
/java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎

Javaの学習を進めていると、?: という記号を使った不思議な記述に出会うことがあります。これは「三項演算子」と呼ばれるもので、if-else文をより簡潔に書くための強力な武器になります。

新人プログラマーが書いたコードをレビューする際、三項演算子を使えばもっとスッキリするのに、と感じる場面が少なくありませんでした。話を聞いてみると、「存在を知らなかった」「使い方がよくわからない」という声がほとんどです。

しかし、Javaの三項演算子は一度覚えてしまえば非常に便利です。適切に使うことで、コードの可読性をぐっと高められます。

この記事では、Javaを学び始めた方や、三項演算子の使い方がいまいちわからない方に向けて、以下の内容を解説します。

  • Java三項演算子の基本的な構文とif-else文との違い
  • 具体的な使い方と実践的な応用例
  • 利用する上でのメリット・デメリットと注意点

この記事を読み終えるころには、あなたは三項演算子を自信を持って使いこなし、よりプロフェッショナルなコードを書けるようになっているでしょう。

Java 三項演算子とは?基本と構文

まずは、Javaの三項演算子がどのようなものなのか、基本的な部分から見ていきましょう。if-else文との違いを理解することが、使いこなすための第一歩です。

三項演算子の定義と読み方

Javaの三項演算子は、その名の通り3つの項目(オペランド)から構成される演算子です。Javaに存在する唯一の三項演算子であり、正式には「条件演算子」と呼ばれます。

この演算子は、ある条件を評価し、その結果がtruefalseかによって、2つの値のうちどちらか一方を選択します。

読み方としては、「もし条件が真ならばこの値を、偽ならばあの値を」と考えるとわかりやすいでしょう。? を「もしそうなら」、: を「そうでなければ」と読み替えると、直感的に理解しやすくなります。

if-else文との違いと使い分け

三項演算子は、機能的に単純なif-else文と似ています。しかし、両者には明確な違いが存在します。

  • if-else文: 条件によって「処理」を分岐させるための「文」です。
  • 三項演算子: 条件によって「値」を選択するための「式」です。

「文」はプログラムの動作を指示するもので、それ自体は値を持ちません。一方、「式」は評価されると何らかの値を返します。

この違いから、使い分けの基本的な方針が見えてきます。

  • 三項演算子を使うべき場面:
    • 条件によって変数に代入する値を変えたいとき
    • 条件によってメソッドの引数や戻り値を変えたいとき
    • 処理が単純で、1行で書ける場合
  • if-else文を使うべき場面:
    • 条件によって複数の処理を実行したいとき
    • 条件分岐のロジックが複雑なとき
    • 可読性を考慮して、処理の流れを明確に示したい場合

Javaの三項演算子は、値を返す「式」であるという点をしっかり押さえておくことが重要です。

三項演算子の基本的な構文

Java三項演算子の構文は非常にシンプルです。

条件式 ? trueの場合の値 : falseの場合の値

それぞれの要素を分解して見てみましょう。

  1. 条件式: 評価結果がtrueまたはfalseboolean型になる式を記述します。例えば score >= 60 のような比較式です。
  2. ?: 条件式の結果がtrueだった場合に、次に続く「trueの場合の値」を選択することを示します。
  3. trueの場合の値: 条件式がtrueのときに、この三項演算子全体が返す値です。
  4. :: 条件式の結果がfalseだった場合に、次に続く「falseの場合の値」を選択することを示します。
  5. falseの場合の値: 条件式がfalseのときに、この三項演算子全体が返す値です。

具体的なコードで確認してみましょう。テストの点数が60点以上なら「合格」、そうでなければ「不合格」という文字列を返す処理を考えます。

if-else文の場合

int score = 75;
String result;
if (score >= 60) {
    result = "合格";
} else {
    result = "不合格";
}
System.out.println(result); // "合格" と表示される

Java三項演算子の場合

int score = 75;
String result = score >= 60 ? "合格" : "不合格";
System.out.println(result); // "合格" と表示される

if-else文では5行必要だった処理が、Javaの三項演算子を使うとわずか1行で記述できました。非常に簡潔で、意図も明確です。

三項演算子の使い方

基本的な構文を理解したところで、次はより具体的な使い方を学んでいきましょう。条件式の書き方や値の型、ネスト構造について解説します。

条件式の書き方

三項演算子の「条件式」部分には、評価結果がboolean型になる式であれば何でも記述可能です。

比較演算子を使った例

int age = 20;
String message = age >= 20 ? "成人です" : "未成年です";
System.out.println(message); // "成人です"

boolean型の変数を直接使う例

boolean isMember = true;
int fee = isMember ? 1000 : 1500;
System.out.println("料金は" + fee + "円です"); // "料金は1000円です"

論理演算子(&&||)を組み合わせる例

複数の条件を組み合わせることもできます。例えば、「天気が晴れ」かつ「気温が25度以上」の場合に「お出かけ日和」と判定する処理です。

boolean isSunny = true;
int temperature = 28;

String weatherReport = (isSunny && temperature >= 25) ? "お出かけ日和です" : "室内で過ごしましょう";
System.out.println(weatherReport); // "お出かけ日和です"

このように、if文の条件式に書けるものは、Javaの三項演算子の条件式にも同様に記述できます。

戻り値の型について

三項演算子を使う上で重要なルールが、「trueの場合の値」と「falseの場合の値」の型です。原則として、これらの型は一致させる必要があります。

もし型が異なる場合、Javaのコンパイラは暗黙的な型変換を試みます。

例えば、int型とdouble型を混在させた場合を考えてみましょう。

int a = 10;
double b = 20.5;
boolean condition = true;

// int型とdouble型が混在している
// この場合、より表現範囲の広いdouble型に統一される
Object result = condition ? a : b; 
System.out.println(result.getClass()); // class java.lang.Double と表示される

この例では、int型の adouble型に変換され、三項演算子全体が返す値の型はdoubleになります。

しかし、互換性のない型同士を組み合わせるとコンパイルエラーになります。詳細は後述の「注意点」で解説します。基本的には、2つの値の型をそろえることを意識するのが安全です。

ネスト(入れ子)構造での利用

Javaの三項演算子は、入れ子(ネスト)にして使うことも可能です。これにより、if-else if-elseのような3つ以上の分岐を表現できます。

例えば、点数に応じて「優」「良」「可」の3段階で評価する処理を考えてみましょう。

int score = 85;

String grade = score >= 80 ? "優"
             : score >= 60 ? "良"
             : "可";

System.out.println(grade); // "優"

このコードは、以下のようなif-else if-else文と等価です。

int score = 85;
String grade;
if (score >= 80) {
    grade = "優";
} else if (score >= 60) {
    grade = "良";
} else {
    grade = "可";
}
System.out.println(grade);

三項演算子をネストさせると、さらにコードを短くできます。しかし、ネストが深くなるとコードの可読性が著しく低下するため、一般的には推奨されません。ネストは1段階までにとどめ、それ以上に複雑になる場合は素直にif-else文を使うのが賢明です。

Java 三項演算子のメリット・デメリット

Javaの三項演算子は便利なツールですが、万能ではありません。メリットとデメリットを正しく理解し、適切な場面で使うことが重要です。

コードの可読性向上効果

三項演算子の最大のメリットは、シンプルな条件分岐においてコードの可読性を向上させる点です。

if-else文の例

int userType = 1; // 1:管理者, 2:一般ユーザー
String permission;
if (userType == 1) {
    permission = "書き込み可能";
} else {
    permission = "読み取り専用";
}

このコードは、userType の値によって permission という変数に異なる値を設定する、という意図です。

Java三項演算子の例

int userType = 1;
String permission = (userType == 1) ? "書き込み可能" : "読み取り専用";

三項演算子を使った場合、「userTypeが1なら書き込み可能、そうでなければ読み取り専用という値をpermissionに代入する」という意図が1行で明確に表現されています。

このように、ある条件に基づいて1つの変数へ値を代入するようなケースでは、三項演算子の方がif-else文よりも直感的で理解しやすくなります。

コード量の削減効果

前述の例からも明らかなように、Javaの三項演算子はコード量を大幅に削減できます。if-else文では最低でも4〜5行必要になる処理が、三項演算子なら1行で完結します。

コード量が少ないことは、単にファイルサイズが小さくなるだけでなく、以下のような利点をもたらします。

  • 一度に目に入る情報量が増え、コード全体の見通しが良くなる
  • スクロール量が減り、コードを読む際の負担が軽減される

もちろん、無理に1行に詰め込むのは良くありません。しかし、適切に使われた三項演算子は、コードをより洗練されたものにします。

複雑な条件式での可読性低下

一方で、Java三項演算子にはデメリットも存在します。それは、複雑な条件分岐に使おうとすると、かえって可読性が低下してしまう点です。

先ほど紹介したネスト構造が良い例です。

// あまり良くない例
String message = (isLogin && hasPermission) ? "ようこそ!"
                 : (!isLogin) ? "ログインしてください"
                 : "権限がありません";

このコードが何をしているのか、瞬時に理解するのは難しいでしょう。条件式や返す値が長くなったり、ネストが深くなったりすると、どこが条件でどこが値なのか判別しにくくなります。

このような場合は、無理に三項演算子を使わず、if-else文で素直に書く方が、誰にとっても読みやすいコードになります。

// こちらの方が読みやすい
String message;
if (isLogin && hasPermission) {
    message = "ようこそ!";
} else if (!isLogin) {
    message = "ログインしてください";
} else {
    message = "権限がありません";
}

「シンプルに書けるなら三項演算子、複雑になるならif-else文」という判断基準を持つことが大切です。

Java 三項演算子の注意点とエラー回避

Javaの三項演算子を安全に使うためには、いくつかの注意点を知っておく必要があります。特に、型に関するエラーやNullPointerExceptionは、初心者が陥りがちな落とし穴です。

型が一致しない場合のエラー

「戻り値の型について」で触れましたが、「trueの場合の値」と「falseの場合の値」は、型に互換性がないとコンパイルエラーになります。

コンパイルエラーになる例

// String型とint型は互換性がないためエラーになる
Object result = (1 > 0) ? "正解" : 100; // コンパイルエラー

このコードをコンパイルしようとすると、「型が一致しません: intString に変換できません」といった主旨のエラーメッセージが表示されます。

このエラーを回避するには、両方の値の型を合わせる必要があります。例えば、数値を文字列に変換する方法が考えられます。

// どちらもString型に統一する
String result = (1 > 0) ? "正解" : String.valueOf(100); 

三項演算子を使う際は、2つの戻り値の型が一致しているか、または互換性があるかを常に意識しましょう。

NullPointerExceptionについて

Javaの三項演算子を使う際に、特に注意が必要なのがNullPointerExceptionです。これは、nullの可能性があるオブジェクトを扱っている場合に発生しやすくなります。

特に、プリミティブ型とそのラッパークラスが混在している場合に問題が起こります。

NullPointerExceptionが発生する例

Integer number = null;
boolean condition = false;

// conditionがfalseの場合、number(null)がint型に変換されようとして
// NullPointerExceptionが発生する
int result = condition ? 1 : number;

このコードは、conditionfalse の場合に number の値を result に代入しようとします。result はプリミティブ型の int ですが、number はラッパークラスの Integer 型で、値は null です。

三項演算子は、trueの場合の値(int)とfalseの場合の値(Integer)の型を統一しようとします。この場合、JavaはInteger型のnumberint型に変換しようとします。この変換を「オートアンボクシング」と呼びます。

しかし、nullであるオブジェクトに対してオートアンボクシングを行うことはできず、NullPointerExceptionという実行時エラーが発生してしまうのです。

この問題を回避するには、以下のような対策が考えられます。

  • resultの型をIntegerにする
  • numbernullの場合のデフォルト値を設定する
// 対策1: resultの型をIntegerにする
Integer result1 = condition ? 1 : number; // これならOK

// 対策2: nullの場合のデフォルト値を設定する
int result2 = condition ? 1 : (number != null ? number : 0);

ラッパークラスを三項演算子で扱う際は、nullの可能性を常に念頭に置き、慎重にコーディングする必要があります。

可読性を損なわない書き方

Javaの三項演算子は、コードを簡潔にする一方で、使い方を誤ると可読性を著しく損ないます。読みやすいコードを保つためのいくつかのガイドラインを紹介します。

  1. シンプルな条件に限定する: 条件式が複雑になったり、長くなったりする場合は、if-else文を検討しましょう。
  2. ネストは避ける: 三項演算子のネストは、基本的に避けるべきです。どうしても必要な場合でも、1段階までにとどめましょう。
  3. 改行とインデントを適切に使う: 1行が長くなる場合は、?:の後で改行し、インデントをそろえると見やすくなります。
// 1行が長い場合の整形例
String result = veryLongConditionName(param1, param2)
              ? "非常に長いTrueの場合の戻り値"
              : "非常に長いFalseの場合の戻り値";
  1. 複雑な式は事前に変数化する: 条件式や戻り値の計算が複雑な場合は、それらを事前に別の変数に入れておくと、三項演算子自体はシンプルに保てます。
// 事前に変数化する例
boolean isEligible = checkAge(user) && hasLicense(user);
String messageForEligibleUser = createEligibleMessage(user);
String messageForIneligibleUser = createIneligibleMessage(user);

String finalMessage = isEligible ? messageForEligibleUser : messageForIneligibleUser;

これらのルールを守ることで、Javaの三項演算子のメリットを最大限に活かしつつ、可読性の高いコードを維持できます。

Java 三項演算子の応用例と実践

最後に、実際の開発現場でJavaの三項演算子がどのように使われるのか、具体的な応用例を見ていきましょう。変数への代入だけでなく、様々な場面で活用できます。

変数への代入

これは最も基本的で、最もよく使われるパターンです。この記事でも何度も例として登場しました。

条件に応じて初期値を設定したい場合に非常に役立ちます。

// ユーザーが管理者かどうかで、表示するダッシュボードのURLを変える
boolean isAdmin = user.getRole().equals("ADMIN");
String dashboardUrl = isAdmin ? "/admin/dashboard" : "/user/dashboard";

if-else文を使うと、変数の宣言と代入が別々になりがちですが、三項演算子なら宣言と同時に初期化できるため、コードがまとまります。

メソッドの引数

メソッドを呼び出す際の引数に、Javaの三項演算子を直接使うこともできます。これにより、引数のためだけに一時的な変数を用意する必要がなくなります。

例えば、ページの表示件数を設定するメソッドを考えてみましょう。リクエストパラメータで件数が指定されていればその値を、なければデフォルト値の20を使いたいとします。

if-else文の場合

String countParam = request.getParameter("count");
int limit;
if (countParam != null) {
    limit = Integer.parseInt(countParam);
} else {
    limit = 20;
}
List<Item> items = itemService.getItems(limit);

Java三項演算子を引数に使う場合

String countParam = request.getParameter("count");
List<Item> items = itemService.getItems(countParam != null ? Integer.parseInt(countParam) : 20);

一時変数limitが不要になり、非常にスッキリとしたコードになりました。メソッド呼び出しの文脈で、条件に応じた値を直接渡せるのは、三項演算子の大きな利点です。

return文での利用

メソッドの戻り値を条件によって変えたい場合にも、Javaの三項演算子は活躍します。return文で直接使用することで、コードを簡潔にできます。

2つの数値のうち、大きい方を返すmaxメソッドを実装してみましょう。

if-else文の場合

public int max(int a, int b) {
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

Java三項演算子をreturn文で使う場合

public int max(int a, int b) {
    return a > b ? a : b;
}

メソッドの意図が「abを比較して大きい方の値を返す」ことであると、一目で理解できます。このように、メソッドの最終的な戻り値を決定するような場面で、三項演算子は非常に効果的です。

まとめ

Javaの三項演算子は、コードをより簡潔で読みやすくするための強力な機能です。

この演算子は、単純なif-else文を置き換えることで、コード行数を削減し、値の選択という意図を明確に表現できます。特に、変数への代入、メソッドの引数、return文といった場面でその真価を発揮するでしょう。

ただし、その力を最大限に引き出すためには、使いどころの見極めが不可欠です。複雑なロジックやネスト構造に無理に適用すると、かえって可読性を損なう原因にもなります。

「この分岐は、ある条件に基づいて2つの値のどちらかを選択するだけか?」

この問いを自問し、答えが「Yes」であれば、それは三項演算子の出番です。一方で、処理の流れが複雑になる場合は、迷わずif-else文を選びましょう。

この使い分けを意識することで、あなたのJavaコードは一段と洗練され、プロフェッショナルな品質に近づいていくはずです。

  • この記事を書いた人
  • 最新記事

トム

・都内自社開発IT企業勤務/javaのバックエンドエンジニア
/java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎

-Java入門