Why not login to Qiita and try out its useful features?

We'll deliver articles that match you.

You can read useful information later.

148
100

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2018-02-19

はじめに

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

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

何がダメか

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

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

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

理由を3つ書きます。

理由1: 冗長である

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

これを読み解くと以下のようになります6。長々と書いていますが、要は命令が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. JavaScriptの場合: Logical Operators - JavaScript | MDN

  2. Javaコーディング規約 – Future Enterprise Coding Standards - フューチャーアーキテクト

  3. ヨーダ記法 - Wikipediaなど

  4. QBA: 論理式で boolean リテラル値を代入しているメソッド (QBA_QUESTIONABLE_BOOLEAN_ASSIGNMENT)

  5. 一応計測しましたが差は確認できませんでした。なので「理論上は」です。

  6. The Java® Virtual Machine Specificationから推測。

148
100
17

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
ikemo

@ikemo(Hideki Ikemoto)

2019/05から転職しました。
Linked from these articles

Comments

felis
@felis

その通り、明らかに冗長で無駄なのです。
それなのにどうしてこの様なコードが世の中から消えてなくならないかというと、「設計書には『someObject.isEnable()がtrueの時』と書け。そしてそれに忠実に『if (someObject.isEnable() == true)』とコーディングしろ」という頭痛が痛くなってフランスに渡米しそうなルールがIT業界の一部でいまだに蔓延っているからなのです…
(業務的に意味のある変数名を付けようとしたらクラス名をキャメルにしたシステムハンガリアンにしろと言われたりするのもあるあるネタ)

そんな古代のルールはいい加減捨てて欲しいですね:put_litter_in_its_place:

8
tsuyoshi_cho
@tsuyoshi_cho(Tsuyoshi CHO)

まあ

  • Cの慣習をひきずった場合(Cはあくまで整数だったので、うっかり動作があったりフラグとかビット演算の関係とか)
  • 複合だとフラグの真/偽だけでand/orを書くとわかりにくくなる、そこから単品でのa==true/falseをやってしまう

なんかの理由はあるかな...、よほどでなければ目くじらは立てず、正しく動くように作ってあるほうが優先されるんじゃないかな(というかしそうです>自分)

あと、遅くなるのは、コンパイラが賢ければ最適化される話ではあるかも

3
taptappun
@taptappun(こばやし たく)

Java以外の言語、例えば、Javascriptではif文の中にboolean以外を入れることができます。(Ruby, pythonもですかね)
このような言語において、nullやundefinedなどもif文で制御するというのはよくあります。
そのため == true がないと逆にわかりづらかったといったことは多くありましたね。
今回のこのような話が当てはまるとするならば、Javaなどのように型が決まっている言語でのお話だけだと思います。
(個人的には言語の特性に依存した書き方をすると他言語になれた人が混乱するので、たとえ冗長であれ、== true とわざと書くこともあります)

3
suzuq
@suzuq

私もこれ、いくつかの現場で主張しましたが、なぜか受け入れてもらえませんでした。
コメントで触れられている通り、言語による部分はありますし、目くじら立ててレビューで指摘するような事はしませんが。
個人的には、「頭痛が痛い」みたいな文章を読んでいるようで気持ちが悪いですね。

1
@error_401

個人的には、if (hoge == 10)に不自然さを感じないのと同様、if (hoge == true)にも不自然さを感じません。
この話のは、1990年代中頃のC FAQの「if ((hoge == fuga) == TRUE)と書くな」という話からの変形だと思っていて、C FAQのそれは確かに冗長だが、if (hoge == true)は冗長だとは思いません。読みやすいし。

3
ikemo
@ikemo(Hideki Ikemoto)

コメントありがとうございます。少しずつ回答していきます。
とりあえず、明らかに間違っている箇所については修正・撤回しました。

0
@lovee(星野 恵瑠)
(Edited)

Swift も当てはまらないのではないかと思います:

理由1: 冗長である

JavaScript と違って確かに Bool 以外の判定は文法上不可能なので、確かに flag == true は冗長な気もしますが、ただ Swift の場合、!== false 以外にも、Optional の Forced Unwrapping の機能があり、後者は比較的に危険な操作であるがゆえに、なるべく後者の方を目立たせた方が保守しやすい面があります(まあそもそも flag とかいう変数名自体がSwiftのコーディング規約的にアウトではありますので isXxx とか hasXxx とか使った方がいいですが、ただ別に if isXxx == true と書いてもそこまで気にはならないですかね)

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

こちらは Swift の文法上不可能であり、間違って if (flag = true) と書いてしまうとコンパイルエラーになりますので Swift においてはこれは不成立です

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

こちらは Debug ビルドは把握していませんが、少なくとも Release ビルドでは確実に最終的に同じバイナリーが書き出されますので、こちらも Swift においては不成立です

2
@kobalab(小林 聡)
(Edited)

true / false 以外の値で真偽を表すことができる言語の場合、flag == true のような書き方を強制することはバグの元になります。なので主張されていることは正しいです。
例えばJavaScriptでは undefined, null, 0, "" も「偽」で、偽でない値はすべて「真」になります。多くのライブラリ関数はこれを利用しており、例えば String オブジェクトのメソッド match() は、マッチが成功した場合に配列を返し(これは真と解釈される)、失敗した場合は null (偽と解釈される)を返します。ですので、
if (str.match(re) == true) と書いてしまうと期待した動作とならず、
if (str.match(re)) と書かなければなりません。

3
ikemo
@ikemo(Hideki Ikemoto)

他の言語について不注意に言及するのはダメですね(汗)
ドラフトはタイトルに「Java」が付いてなかったのですが、付けておいてよかった。。。
JavaScriptは得意ではないですが、自分は===で比較するのが好きですね。

flagという変数名が悪いのは百も承知ですが、これも本題とは外れるので、
「flagという変数名は避けてください」というコメントでご容赦を。

@hiuchida さんの指摘ですが、
確認したところ、flag == falseでも!flagでも同じバイトコードでした。
まあ「余談」ではあるんですが、詳しくないことを書くものじゃないですね。。。

一旦ここで切ります。

0
ikemo
@ikemo(Hideki Ikemoto)

本題。

何で「書いてはいけない」と強い否定表現をしたのかというと、とあるサイトで、意図しない代入 if (isAdmin = true) による問題を避ける方法として、ヨーダ記法 if (true == isAdmin) を挙げていたのを見たからです。

間違った前提、ここでは if (isAdmin) でなく if (isAdmin == true)という書き方を前提として、間違った解決方法(ヨーダ記法)を使うのがおかしい、そのためには、間違った前提で考えるのを止めないといけない、そのために強い否定表現を使いました。

そこまで分かっててあえて別の表現を使うことは否定しませんが、若い人は型を守る(守破離の「守」)ところから始めないと、後々苦労すると思います。「新人プログラマ応援」というタグを付けたのはそのためです。


あと、C言語のときのクセは、少なくともJavaを使うときには止めたほうがいいと思います。別の記事で書くかもしれませんが、C言語では初期化しないと不定値になるため初期化が推奨されますが(少なくとも1995年頃はそうだった)、Javaは変数を初期化するとむしろ損なケースもあります。なので、他の言語のクセは持ち込まないほうがいいです。

2
totocla
@totocla

個人的には==trueとは書かないけど、別に書いてあっても気になりませんね。なお、冗長である、以外の理由は違うのでは?

0
tomohisaota
@tomohisaota(Tomohisa Ota)

変数名の例がよくないのでなんともいえないのですが、可読性が高い方を選べば良い気がします。
if(option1 == true)
if(isValid)
一律に禁止するレベルの話ではないと思います。

いろんな言語を使っている身としては、下記ルールで統一されていると読みやすいです。

  • if文の中では代入を許さない
  • if文の評価値はブーリアンしか許さない
    Swiftの回し者じゃないです。普段書いてるのはJavaScript,Java,Python。

あと、このレベルの
「(理論的には)遅くなる」
は気にしない方がいいと思います。
今時の言語だと自分が書いたプログラムが最終的にどうやって実行されるかなんて追いきれないですし、追う必要もないと思います。

2
ikemo
@ikemo(Hideki Ikemoto)
(Edited)

obj.getFoo().isBar();の方が一般的ではないですか?

こちらはその通りなので修正しました。

また、isValidFoo()とisInvalidFoo()のように名前を変えるだけで結果が反転しますので、否定の!が目立たないのならば、結果が反対のメソッドを追加できるか検討するというのもアプローチとして考えられると思います。

以下のように実例もありますが、
「使用頻度が高い」か「メソッドチェーンを多用する」という理由で追加すべきであって、
! が目立たないからという理由はちょっとどうかと思います。

  • Objects#nonNull()
  • AssertJ(メソッドチェーンを多用)
  • Apache Commons LangのStringUtils#isNotEmpty()

そこまでしてJavaで ! を避ける理由が知りたいです。。。

0
Kilisame
@Kilisame

タイトルにJavaでは、とされているのであくまでJava限定で話をしますが、Javaの標準API、メジャーなJavaのライブラリ群(apache-commonsやAWSのJavaSDKなど)において、
if (bool変数 == true) の記法を認めているのをほとんど見たことはありません。
(ゼロではないです。例えば、Java8の7000ファイルほどあるソースを全検索したら、過去の遺産的なcom.sunパッケージ以下でいくつかヒットしました。java.パッケージ以下に絞っても3件ありました)

開発者は基本的に、その言語の標準のAPIのソースコード、メジャーなライブラリ、SDKのソースコードと自分たちの作成するソースコード、両者を読みながら開発を進めると思うのです。デバッグで元をたどっていく過程で、自作コードからいつのまにかAPIのコードに上っていくこともあるでしょう。
そんな過程において自作のコード側にのみ、標準APIやメジャーライブラリでは出てこない、無意味で冗長な記法が混ざることは単純に「意図が明確に伝わりにくくなり」「読みにくく」なる要因にしかならないと思います。記法は統一されていた方が望ましく、統一するのならスタンダード側に寄せるのが当たり前じゃないですか?
(うちの開発ではAPIのコード、ライブラリのコードを読んだりしません、というのであれば、好きにしたら良いでしょうが)

全世界の主流のJava開発者が慣れ親しんでいる記法より「こっちの方が自分には読みやすい」とプロジェクトメンバーに主張されても、私なら却下します。
同様の理由で、独自のprefixルールなどの導入にも反対です。APIやライブラリのコードと自作のコードを「異なるルール・記法で書かれたものとして切り替えながら読む」ことは完全に無駄なコストだと思っています。

3
ikemo
@ikemo(Hideki Ikemoto)

念のためこの記事を書いた後にいくつか有名プロジェクトのソースコードを読みましたが、 == true は見つかりませんでした。 == false はElasticsearchで見たくらいです(ちょっとびっくりしましたが、全体的には読みやすいコードです)。

必ずしも有名プロジェクトだから品質の高いコードばかりであるとは言えませんが、スタンダードから外れて「こっちの方が自分には読みやすい」と言っていいのは、守破離の「離」の段階に達した人だけだと思います。

1
kurema
@kurema(暮間 真)
(Edited)

C#だとnull許容値型でbool?が可能だから、if (isAdmin == true){}は普通に使うなぁ。
良くあるのはif(Content?.Items?.Contains(a => a.IsDirectory) == true)みたいなパターン。
boolが返ってくる関数でもnull条件演算子を使ってたら返ってくるのはbool?だし、後からnull対策とかもすることもあるから必要なくても== trueを付けることはある。

他の言語でも似たような例は結構あるんじゃないかな?
Javaにnull条件演算子が導入されたら(もうされたりする?)== trueは氾濫しそう。
後はJavaScriptの例が挙がってるがif条件文内がbool型じゃなくても良い言語なら有無は判断しないといけない。

1
juner
@juner

@kurema
C# だと 直接でも is true でも == true でも 同様の IL の出力になるので問題ないやつですね。

source IL
image.png image.png
image.png image.png

0

Let's comment your feelings that are more than good

Being held Article posting campaign

148
100

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Login to continue?

Login or Sign up with social account

Login or Sign up with your email address