null の問題

ポインターを利用するプログラミング言語での NULL ポインターや、参照型データでの null の取り扱いには多くの問題があります。 歴史的にも多くのプロジェクトで問題を引き起こしており、多くのソフトウェアエンジニアが苦労している問題でもあります。 そのため、null を安全に取り扱うための手法が多く存在します。 ここでは null によって引き起こされる問題を紹介します。

実体が存在しないためオブジェクトとしては利用できない

参照型の変数に null が代入されている場合には実体が存在しないため変数経由でメンバーにアクセスすることができません。

// null 例外の出るコード
List<int> numbers = null;

// この行を実行すると NullReferenceException 例外が発生します
Console.WriteLine("numbers.Count: {0}", numbers.Count);

この C# で記述されたコードでは変数 numbersのメンバーへのアクセスを行うと NullReferenceException が発生します。 参照型の変数に null が代入されている場合には null 以外の値が代入されているときの挙動と異なり、例外が発生することになります。 見方によっては変数に null が代入されている場合には本来の変数の型の挙動を満たしてない様にも見えます。 null の代入された変数に対してアクセスしようとすると NullReferenceException が発生し、最悪の場合プログラムが終了することになります。

null でないことは動的にしか確認できないプログラミング言語が多い

null を安全に取り扱うための文法機能や標準ライブラリを持っていないプログラミング言語では静的に (コンパイル時に) 利用する変数が null なのかどうかをチェックすることができません。 そのため、実行時に null でないことを確認する必要があります。 また、動的にしかチェックできないため、今この変数は null チェックがされた後なのか、される前なのかが機械的には分からず、全ての個所でプログラマーが気を付ける必要があります。 場合によっては null チェックされておらずに NullPointerException を出してしまったり、逆に必要以上の null チェックをしてしまう場合があります。

List<int> numbers = null;

if (numbers == null)
{
    // numbers が null だった場合の処理
}

// ここで numbers が null でないことはプログラマーが直前のソースコードを読んで確認するしかありません
// 例えば、上の if 文の中で numbers へのデフォルト値の代入や retrun などの処理が行われていなかった場合には、この箇所で numbers が null である可能性があります

コードによっては静的解析ツールの助けを借りることによって、事前に検出することも可能です。

List<int> numbers = null;

Console.WriteLine("numbers.Count: {0}", numbers.Count);

この例の様に null を代入した直後にメンバーにアクセスするような場合には、静的解析ツールによって問題を検出することができる場合がありますが、複雑な処理の結果 null が代入されている場合には検出することは困難です。

対処方法

null の取り扱いに関しては昔から問題になっているため以下の様に多くの解決策が存在します。

  • null object
  • Maybe モナド, Option
  • null 安全

参考資料