Javaではif (isAdmin == true)というコードを書いてはいけない


# はじめに

当たり前だと思うのですが、意外と守られていないことがあります。その1つの例として、条件分岐でのbooleanの扱いを挙げてみます。

~~タイトルには「Javaでは」と書きましたが、おそらくどの言語でも同じです。~~JavaScriptなど他の言語では当てはまらないので取り消します[^js]。

# 何がダメか

明確にコーディング規約で否定しているケースもありますが[^1]、Javaでは以下のようなコードは**書いてはいけません**。

```java
if (isAdmin == true) {
  // isAdminがtrueのとき
}

if (isAdmin == false) {
  // isAdminがfalseのとき
}
```

理由を3つ書きます。

# 理由1: 冗長である

上記のコードは以下のように書くことができます。こちらの方がシンプルです。

```java
if (isAdmin) {
  // isAdminがtrueのとき
}

if (!isAdmin) {
  // isAdminがfalseのとき
}
```

`if〜else〜`の場合は三項演算子で書くとシンプルになることもあります。ただし、三項演算子を乱用すると、可読性が落ちる場合があります。詳細は以下の記事を参照してください。

- [Javaで三項演算子はどこまで許されるか - Qiita](https://qiita.com/ikemo/items/f81f08055bb873b4d648)

```java
String userType;
if (isAdmin) {
  userType = "管理者";
} else {
  userType = "一般";
}

// 以下のように三項演算子で書くほうがシンプルになる
String userType = isAdmin ? "管理者" : "一般";
```

trueの場合は問題ないと思うのですが、falseの場合は`!`が目立ちにくいケースがあります。

```java
if (!obj.getFoo().isBar()) {
  // 処理
}
```

こういうときは、説明用変数を追加すると良いでしょう。

```java
boolean bar = obj.getFoo().isBar();
if (!bar) {
  // 処理
}
```

# 理由2: 代入と間違える可能性がある

Javaではif文に書くのはboolean型と決まっているので、if文に代入を書くと通常はコンパイルエラーになります。しかし、boolean型の場合、以下のように書いても、警告が出ずにコンパイルは通ってしまいます。

```java
if (isAdmin = true) {
  // isAdminがtrueのとき・・・のはずが常に実行される
}

if (isAdmin = false) {
  // isAdminがfalseのとき・・・のはずが常に実行されない
}

// これはコンパイルエラーになる
if (isAdmin = 1) {
}
```

だからといって以下のようにヨーダ記法[^yoda]で書くのは**本末転倒**です。

```java
if (true == isAdmin) {
  // isAdminがtrueのとき
}

if (false == isAdmin) {
  // isAdminがfalseのとき
}
```

実際は、IDEやFindBugs/SpotBugs[^3]で警告を出してくれるため、気づかないはずがないのですが、最初から`if (isAdmin == true)`という書き方をしなければ問題ありません。

# 理由3: (理論的には)遅くなる

以下の内容は`b == true`のときのみで、`b == false`や`!b`を使ったときは影響ありません。詳しくはコメントを参照してください。

これはほとんど余談ですが、理論的には無駄な処理が入ってしまいます[^4]。例えば、以下のコードをコンパイルして、`javap -c`で逆アセンブルします。

```java
public class Main {
    public int foo(boolean b) {
        if (b == true) {
            return 1;
        } else {
            return 0;
        }
    }

    public int bar(boolean b) {
        if (b) {
            return 1;
        } else {
            return 0;
        }
    }
}
```

すると、以下のような出力が得られます(関係ない出力は除いています)。

```
  public int foo(boolean);
    Code:
       0: iload_1
       1: iconst_1
       2: if_icmpne     7
       5: iconst_1
       6: ireturn
       7: iconst_0
       8: ireturn

  public int bar(boolean);
    Code:
       0: iload_1
       1: ifeq          6
       4: iconst_1
       5: ireturn
       6: iconst_0
       7: ireturn
```

これを読み解くと以下のようになります[^5]。長々と書いていますが、要は命令が1つ無駄に多くなってしまいます。

なぜなら、`if (b)`は「bが真なら」を表しているのに対し、`if (b == true)`は、「bがtrueと比較した結果が真なら」と回りくどい表現になっているからです。

- foo()
  - 0: ローカル変数1(引数`b`)をスタックに入れる
  - 1: 定数1(おそらく`true`)をスタックに入れる
  - 2: スタックの値(0:と1:)を比較し、違ってたら`7:`に移動
  - 5: 定数1(おそらく`1`)をスタックに入れる
  - 6: メソッドから値を戻す
  - 7: 定数0(おそらく`0`)をスタックに入れる
  - 8: メソッドから値を戻す
- bar()
  - 0: ローカル変数1(引数`b`)をスタックに入れる
  - 1: スタックの値が`0`なら、`6:`に移動
  - 4: 定数1(おそらく`1`)をスタックに入れる
  - 5: メソッドから値を戻す
  - 6: 定数0(おそらく`0`)をスタックに入れる
  - 7: メソッドから値を戻す

# おわりに

「これくらいどっちでもいいじゃないか」という人がいるかもしれませんが、こういうちょっとしたことの積み重ねが、将来大きな差となって表れます。少しずつでも良い習慣をつけていって欲しいです。

# 追記

反響があったようなので少しずつ補足していきます。

- `flag` という変数名は悪い→その通りなので記述を追加しました。
  - それすら読んでもらえないようなので全部書き換えました。
- 他の言語(JavaScriptなど)では当てはまらない→その通りなので取り消しました。
- 三項演算子は読みにくい→ケースバイケースなので、少し表現を弱めました。

[^1]: [Javaコーディング規約 – Future Enterprise Coding Standards - フューチャーアーキテクト](https://future-architect.github.io/coding-standards/documents/forJava/Java%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0%E8%A6%8F%E7%B4%84.html)
[^3]: [QBA: 論理式で boolean リテラル値を代入しているメソッド (QBA_QUESTIONABLE_BOOLEAN_ASSIGNMENT)](https://spotbugs.readthedocs.io/ja/latest/bugDescriptions.html#qba-boolean-qba-questionable-boolean-assignment)
[^4]: 一応計測しましたが差は確認できませんでした。なので「理論上は」です。
[^5]: [The Java® Virtual Machine Specification](https://docs.oracle.com/javase/specs/jvms/se8/html/index.html)から推測。
[^yoda]: [ヨーダ記法 - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%A8%E3%83%BC%E3%83%80%E8%A8%98%E6%B3%95)など
[^js]: JavaScriptの場合: [Logical Operators - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_Operators)