web-dev-qa-db-ja.com

Javaでnullを処理する最良の方法?

NullPointerExceptionのために失敗しているコードがあります。オブジェクトが存在しないオブジェクトでメソッドが呼び出されています。

しかし、これは私にこれを修正する最良の方法を考えるようにさせました。常にnullを防御的にコーディングして、nullポインター例外のコードを将来的に証明するか、それとも下流で発生しないようにnullの原因を修正する必要がありますか?.

あなたの考えは何ですか?

21
Shaun F

Nullがメソッドの妥当な入力パラメーターである場合は、メソッドを修正してください。そうでない場合は、発信者を修正してください。 「合理的」とは柔軟な用語であるため、次のテストを提案します。メソッドはnull入力をどのように処理する必要があるか。考えられる答えが複数ある場合、nullはnotが妥当な入力です。

45
user281377

nullは使用せず、オプションを使用してください

あなたが指摘したように、Javaでのnullの最大の問題の1つは、どこでも使用できる、または、少なくともすべての参照タイプについて。

それがnullである可能性があることと、そうでない可能性があることを区別することは不可能です。

Java 8では、はるかに優れたパターンOptionalが導入されています。

Oracleの例:

String version = "UNKNOWN";
if(computer != null) {
  Soundcard soundcard = computer.getSoundcard();
  if(soundcard != null) {
    USB usb = soundcard.getUSB();
    if(usb != null) {
      version = usb.getVersion();
    }
  }
}

これらのそれぞれが正常な値を返す場合と返さない場合がある場合は、APIをOptionalsに変更できます。

String name = computer.flatMap(Computer::getSoundcard)
    .flatMap(Soundcard::getUSB)
    .map(USB::getVersion)
    .orElse("UNKNOWN");

型のオプション性を明示的にエンコードすることにより、インターフェースが大幅に改善され、コードがよりクリーンになります。

Java 8を使用していない場合は、com.google.common.base.Optional Googleグアバ。

グアバチームによる良い説明: https://github.com/google/guava/wiki/UsingAndAvoidingNullExplained

複数の言語の例を使用して、nullの欠点のより一般的な説明: https://www.lucidchart.com/techblog/2015/08/31/the-worst-mistake-of-computer-science/


@ Nonnull、@ Nullable

Java 8は、これらの注釈を追加して、IDEなどのコードチェックツールが問題を検出できるようにします。それらの効果はかなり限られています。


それが理にかなっているときにチェックしてください

特にnull値を使用してコードで実行できる適切な処理がない場合は、nullをチェックするコードの50%を記述しないでください。

一方、nullを使用して何かを意味する可能性がある場合は、必ず使用してください。


結局のところ、Javaからnullを削除することはできません。可能な限りOptional抽象化を代用し、それについてnullをチェックして、それについて妥当なことを行うことができる場合はそれを強くお勧めします。

21
Paul Draper

これを処理するには複数の方法があります。if (obj != null) {}でコードをペパーリングすることは理想的ではありません。面倒で、後でメンテナンスサイクル中にコードを読み取るときにノイズを追加し、忘れがちなのでエラーが発生しやすくなります。このボイラープレートのラッピングを行います。

コードを静かに実行し続けるか失敗させるかによって異なります。 nullはエラーまたは予期される状態ですか。

nullとは

すべての定義とケースで、 Null はデータの完全な欠如を表します。データベースのNullは、その列に値がないことを表します。 null Stringは空のStringと同じではありません。nullintは理論的にはZEROと同じではありません。実際には「依存する」。空のStringは、Stringクラスの_Null Object_実装に適しています。Integerは、ビジネスロジックに依存します。

代替案は次のとおりです:

  1. _Null Object_ パターン。 null状態を表すオブジェクトのインスタンスを作成し、その型へのすべての参照をNull実装への参照で初期化します。これは、nullである可能性があり、有効な状態としてnullであることが期待される他のオブジェクトへの参照が多くない単純な値タイプのオブジェクトに役立ちます。

  2. アスペクト指向ツールを使用して、パラメーターがnullになるのを防ぐ_Null Checker_アスペクトを持つメソッドを作成します。これは、nullがエラーである場合です。

  3. assert()if (obj != null){}よりも優れていますが、ノイズは少ないです。

  4. Javaの契約 などの契約執行ツールを使用します。 AspectJのようなものと同じユースケースですが、より新しく、外部構成ファイルの代わりにアノテーションを使用します。アスペクトとアサートの両方の作品のベスト。

1は、着信データがnullであることがわかっていて、上流のコンシューマーがすべてのnullチェックボイラープレートコードを処理する必要がないように、デフォルト値で置き換える必要がある場合の理想的なソリューションです。既知のデフォルト値に対するチェックもより表現力があります。

2、3、および4は、NullPointerExceptionをより有益なものに置き換えるための便利な例外ジェネレーターです。これは常に改善されています。

終わりに

null in Javaは、ほとんどすべての場合で論理エラーです。常にNullPointerExceptionsの根本原因を排除するよう努める必要があります。null条件をビジネスロジックとして使用しないように努める必要があります。if (x == null) { i = someDefault; }デフォルトのオブジェクトインスタンスに初期割り当てを行うだけです。

8
user7519

Nullチェックを追加すると、テストが問題になる場合があります。この素晴らしい講義をご覧ください...

Google Techトークの講義をご覧ください:「クリーンなコードトーク-物事を探すな!」彼は24分あたりにそれについて話します

http://www.youtube.com/watch?v=RlfLCWKxHJ0&list=PL693EFD059797C21E

偏執狂的なプログラミングでは、どこにでもnullチェックを追加する必要があります。最初は良い考えのように思えますが、テストの観点からすると、nullチェック型のテストを処理するのが難しくなります。

また、次のようなオブジェクトが存在するための前提条件を作成すると、

class House(Door door){

    .. null check here and throw exception if Door is NULL
    this.door = door
}

例外をスローするため、ハウスを作成できなくなります。あなたのテストケースがドア以外のものをテストするためのモックオブジェクトを作成していると仮定してください、まあ、ドアが必要なのでこれを行うことはできません

モック作成地獄で苦しんでいる人たちは、これらのタイプの厄介さをよく知っています。

要約すると、テストスイートはドア、家、屋根など何でもテストするのに十分なほど堅牢で、それについて偏執狂する必要はありません。テストでは、特定のオブジェクトに対してnullチェックテストを追加するのはどれほど難しいですか。

動作するアプリケーションを優先する必要があります。動作することを証明するいくつかのテストがあるからです。HOPINGが動作するのは、すべての場所に前提条件のnullチェックがたくさんあるからといって単純に機能するからです。

8
Constantin

tl; dr-予期しないnullsをチェックするのは良いことですが、アプリケーションがそれらを有効にしようとするのは悪いことです。

詳細

明らかに、nullがメソッドへの有効な入力または出力である状況と、そうでない状況があります。

ルール#1:

nullパラメータを許可するか、null値を返すメソッドのjavadocmustは、これを明確に文書化し、説明しますnullの意味.

ルール#2:

APIメソッドは、正当な理由がない限り、nullを受け入れるまたは返すように指定しないでください。

nullsに対するメソッドの「契約」の明確な仕様が与えられた場合、それを渡したり返したりするのはプログラミングエラーですnullすべきでない場所。

ルール#3:

アプリケーションは、プログラミングエラーを「適切に」しようとするべきではありません。

メソッドが存在してはならないnullを検出した場合、それを他の何かに変換することによって問題を修正しようとするべきではありません。それはプログラマーから問題を隠すだけです。代わりに、NPEの発生を許可し、障害を引き起こして、プログラマーが根本的な原因を突き止めて修正できるようにする必要があります。うまくいけば、失敗はテスト中に気づかれるでしょう。そうでなければ、それはあなたのテスト方法論について何かを言います。

ルール#4:

可能であれば、プログラミングエラーを早期に検出するコードを記述してください。

コードに多数のNPEが発生するバグがある場合、最も難しいのは、nullの値がどこから来たかを突き止めることです。診断を簡単にする1つの方法は、nullができるだけ早く検出されるようにコードを記述することです。多くの場合、これを他のチェックと組み合わせて行うことができます。例えば.

public setName(String name) {
    // This also detects `null` as an (intended) side-effect
    if (name.length() == 0) {
        throw new IllegalArgumentException("empty name");
    }
}

(明らかに、ルール3と4を現実に調整する必要がある場合があります。たとえば、(ルール3)、ある種のアプリケーションは、後で継続しようとしますおそらくプログラミングエラーを検出します。(ルール4)不正なパラメータをチェックしすぎると、パフォーマンスに影響を与える可能性があります。)

4
Stephen C

メソッドを防御的に修正することをお勧めします。例えば:

String go(String s){  
    return s.toString();  
}

これの線に沿ってもっとあるはずです:

String go(String s){  
    if(s == null){  
       return "";  
    }     
    return s.toString();  
}

これは絶対に取るに足らないことだと思いますが、呼び出し側がオブジェクトを期待している場合は、nullが渡されることのないデフォルトのオブジェクトを提供します。

3
Woot4Moo

Nullに関する次の一般的な規則は、これまでのところ非常に役立ちました。

  1. データがコントロールの外部からのものである場合は、体系的にnullをチェックし、適切に動作します。これは、関数にとって意味のある例外をスローすることを意味します(チェックされているかチェックされていないか、例外の名前が何が起こっているかを正確に示していることを確認してください)。ただし、驚きをもたらす可能性のある値がシステムで失われないようにしてください。

  2. Nullがデータモデルの適切な値のドメイン内にある場合は、適切に処理します。

  3. 値を返すときは、可能な限りnullを返さないようにしてください。空のリスト、空の文字列、Nullオブジェクトパターンを常に優先します。これが特定のユースケースのデータの可能な限り最適な表現である場合は、戻り値としてnullを保持します。

  4. おそらくすべての中で最も重要です...テスト、テスト、そしてもう一度テスト。あなたのコードをテストするとき、それをコーダーとしてテストしないでください、それをナチのサイコパスdominatrixとしてテストして、そのコードから地獄を拷問するあらゆる種類の方法を想像してみてください。

これは、ヌルに関して偏執的な側面に少し傾向があり、システムを外界にインターフェースするファサードとプロキシ、および豊富な冗長性を備えた内部の厳密に制御された値につながることがよくあります。ここの外の世界は、自分でコーディングしなかったほとんどすべてのことを意味します。実行時間はかかりますが、これまでのところ、コードの「ヌルセーフセクション」を作成してこれを最適化する必要はほとんどありませんでした。私は主にヘルスケアのために長時間実行されるシステムを作成し、最後に必要なのは、別のシステムの他の誰かが名前ができることに気づかなかったため、予期しないnullポインターが原因でCTスキャナーにヨウ素アレルギーを運ぶインターフェースサブシステムがクラッシュすることです。アポストロフィまたは但しややのような文字を含む。

とにかく...私の2セント

2
Newtopian

関数型言語のOption/Some/Noneパターンを使用することを提案します。私はJavaスペシャリストではありませんが、C#プロジェクトでこのパターンの独自の実装を集中的に使用しており、Javaに変換できると確信しています世界。

このパターンの背後にある考え方は、値が存在しない可能性がある場合(たとえば、IDでデータベースから取得する場合)に論理的に必要な場合は、オプション[T]のオブジェクトを提供します(Tは可能な値)。 。値が存在する場合、None [T]クラスの値オブジェクトが存在しない場合、Some [T]のオブジェクトが返され、値が含まれています。

この場合、値の不在の可能性を処理する必要がある必要があり、コードレビューを行うと、誤った処理の場所を簡単に見つけることができます。 C#言語の実装からインスピレーションを得るために、私のbitbucketリポジトリを参照してください https://bitbucket.org/mikegirkin/optionsomenone

Null値を返し、それがエラーと論理的に同等である場合(たとえば、ファイルが存在しない、または接続できなかった場合)、例外をスローするか、別のエラー処理パターンを使用する必要があります。この背後にあるアイデアは、あなたがmust値の不在の状況を処理し、誤った処理の場所を簡単に見つけることができる場合に、解決策に終わりますコードで。

1
Hedin
  1. 本当にオプションである場合を除き、オプションタイプを使用しないでください。通常、バグのあるコードを定期的に記述するためではなく、オプションとしてnullを期待している場合を除いて、出口は例外としてよりよく処理されます。

  2. この問題は、Googleの記事で指摘されているように、その用途がある型としてnullではありません。問題は、nullをチェックして処理する必要があることです。多くの場合、nullは正常に終了できます。

  3. プログラムの通常の操作以外の領域で無効な条件(無効なユーザー入力、データベースの問題、ネットワーク障害、ファイルの欠落、データの破損)を表すnullのケースがいくつかあります。記録するだけの場合。

  4. 例外処理は、オプションのようなタイプとは対照的に、JVMでさまざまな操作上の優先度と特権が許可されています。これは、ハンドラーのメモリへの優先的な遅延読み込みの早期サポートなど、発生の例外的な性質に意味があります。それはいつもぶらぶらする必要があります。

  5. 信頼性の低いデータサービスにアクセスしている可能性のある場所や、信頼性の低いデータサービスにアクセスしている可能性のある場所にのみ、どこにでもnullハンドラーを作成する必要はありません。まれな場合を除いて、ハンドラー呼び出し。

だから私の答えはそれをチェックされた例外にラップして処理するか、それがあなたの能力の範囲内であれば信頼できないコードを修正することだと思います。

0
J-Boss

メソッドの考えられる結果の1つがnull値である場合は、それを防御的にコーディングする必要がありますが、メソッドがnull以外を返すことが想定されているがそうでない場合は、確実に修正します。

いつものように、人生のほとんどのものと同様に、それはケースに依存します:)

0
jonezy

Apache Commonsの lang ライブラリはnull値を処理する方法を提供します

クラス defaultIfNull クラスのメソッド ObjectUtils では、渡されたオブジェクトがnullの場合にデフォルト値を返すことができます

0
Mahmoud Hossam