地平線に行く

2013-04-14

「null」をフラグとして使うのは、やめた方がいい

| 16:45 |

null をフラグとして使うのは、やめた方がいいと思います。


null は、ただ変数初期化されていないことを表しているだけです。

この意味以外で、null を使わない方がいいと思います。


null をフラグとして使う

「null をフラグとして使う」というのは、「null なら xxxx」というように、null が何らかの意味を持って使われていることを指します。

例えば、下記のコードでは「null はゲストユーザを指すフラグ」として使われています。

/**
 * ユーザ用のヘッダを作る処理
 */
public String createHeader(User user){
    (…中略…)
    String name;
    if(user != null){
        name = user.getName();
    }else{
        name = "Guest";    // null ならゲストユーザ
    }

問題点

これの問題点は、null が何を意味しているのか、すぐに分からないという点です。

例えば、以下のコードを見たときにnull の意味をすぐに理解できるでしょうか*1

    String header = createHeader(null);

null をフラグとして使うときの問題点はここです。

それぞれの処理において好き勝手に意味を定義して null を使用すると、コードを読むときにそれぞれの処理で null が何を意味しているかを理解する必要がでてきてしまいます。


また、上記のコードに続きがあったらどうでしょうか。

    String header = createHeader(null);
    String body = createBody(content, null);

null が2回出てきていますが、「createHeader の引数の null 」と、「createBody の引数の null 」は意味が同じであるとは限りません。

しかし、このコードだけではそれを理解できません*2

また、仮に理解していたとしても、読むたびに「最初の null はこういう意味で、次の null はこういう意味で…」と思い返さなければいけません。



このように、null をフラグとして使用すると、コードが途端に難しくなってしまいます。


解決策1

例えば、最初の処理であれば「ゲストユーザ」というオブジェクトを用意すれば、null をなくせます。

    /**
     * ゲストユーザ
     */
    private static final User GUEST = new User() {
        @Override
        public String getName() {
            return "Guest";
        }
    };

    /**
     * ゲストユーザを取得します。
     * 
     * @return ゲストユーザ
     */
    public static User getGuest() {
        return GUEST;
    }

これを使うと、先ほどの処理は以下のようになります。

    String header = createHeader(User.getGuest());

これなら、「ゲストユーザ用のヘッダーを作っている」というのが理解しやすいと思います。


また、createHeader メソッドの処理もすっきりさせることができます。

public String createHeader(User user) {
    (…中略…)
    String name = user.getName();

解決策2

メソッドオーバロードして、null を局所的な範囲にとどめるという手もあります。

    public String createBody(Content content){
        createBody(content, null)
    }

    public String createBody(Content content1, Content content2){
        (…中略…)
    }

これなら、先ほどのコードは以下のように書き直せます。

    String header = createHeader(User.getGuest());
    String body = createBody(content);    // null を書かなくて済む!

まとめ

null をフラグとして使うべきではないと思います。

そのように null を使ってしまうと、コードをとても難しくしてしまうからです。


それを避ける方法は状況に応じて様々で、一概には解決できないやっかいな問題です*3

それでも null をフラグとして使っているのを見かけたら、どうすればそれを避けることができるのか、考えてみる必要があるのではないかと思います。

*1:createHeader(user) と書かれていたなら「ユーザ用のヘッダーを作っている」と理解できそうですが、null だとそうはいきません。

*2:null という単語から想像できることは、冒頭に挙げた「変数初期化されていないことを表している」だけです。

*3:今回挙げた解決策だけでは、おそらくすべてのパターンを解消することはできないと思います。

testes 2013/04/15 10:38 「nullを未定義以外の意味で使うと分かりづらい」ことと
「nullをフラグとして使うべきではない」ことには何ら因果関係がないと思いますが。
正しい意味でnullを使うなら混同することもありません。

ef3ef3 2013/04/15 10:52 例示されているケースは、Singletonパターンを使うとよさそうなケースですね。
Singletonパターンで単一のインスタンスを生成することを保証する為に、まさに「未初期化だったらnull」を利用することがあります。

y_nakanishiy_nakanishi 2013/04/15 14:17 ある状態をnullで表現すると、バグを作りやすくなるだけで利点はないですからね。

SiroKuroSiroKuro 2013/04/15 14:49 Object obj = null;
とした時に「objは初期化されてない!」って思う時点でなんか違うと思ってます。
javaのnullは未定義値ではなかったはず。

makimaki 2013/04/15 19:12 例示のコードなら、例外時はゲストが返る、以上の意味を見出せませんね。

このコードから読み取れるのは、「ゲストにするときはnullにしなさい」ではなくて、「万が一nullでもゲストになる」、です。
当然運用上はゲストのユーザーも定義するでしょうし、ゲストを使用したいときは、その定義済みユーザーでコールするでしょう。

規定外の動作で、なんらか値を返すべきか、エラーとすべきかはシチュエーションで諸々でしょうが、それは設計で解決すべき話です。
仮に、例示のケースで、ゲストを呼ぶためにnullでコールした人がいたとしたら、きちんとゲストユーザーを使用しなさい、と言うべき。