Javaでヌルを処理する最良の方法は?[閉まっている]


21

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

しかし、これにより、これを修正する最良の方法を考えるようになりました。nullポインタ例外のコードを将来的に保証するために、nullに対して常に防御的にコーディングするか、nullの原因を修正してダウンストリームで発生しないようにする必要がありますか。

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


5
なぜ「nullの原因を修正しない」のですか?これだけが賢明な選択ではない理由の例を提供できますか?明らかに、何かが明白なものからあなたを押しやるに違いない。それは何ですか?なぜ根本的な原因を修正しないのが最初の選択なのですか?
S.Lott

問題の「根本原因」を適切に修正することは短期的には役立つかもしれませんが、コードがまだnullを防御的にチェックしておらず、nullを導入する可能性のある別のプロセスで再利用される場合は、再度修正する必要があります。
ショーンF

2
@Shaun F:コードが壊れている場合、壊れています。コードで予期しないヌルが生成され、修正されない可能性はありません。明らかに、いくつかの「ダム管理」または「政治的」なものが進行中です。または、誤ったコードが何らかの形で受け入れられるようにするもの。エラーのあるコードが修正されていないことを説明できますか?この質問をするように導いた政治的背景は何ですか?
-S.ロット

1
「後でヌルを含むデータ作成に対して脆弱」それはどういう意味ですか?「データ作成ルーチンを修正できる」場合、それ以上の脆弱性はありません。この「nullを含む後のデータ作成」が何を意味するのか理解できませんか?バギーソフトウェア?
-S.Lott

1
@ S.Lott、カプセル化、単一責任。すべてのコードは、他のすべてのコードが何をするかを「知っている」わけではありません。クラスがFroobinatorsを受け入れる場合、Froobinatorの作成者と作成方法について、できる限り仮定を行いません。それは「外部」コードに由来するか、コードベースの別の部分に由来します。バグのあるコードを修正すべきではないと言っているわけではありません。「ディフェンシブプログラミング」を検索すると、OPが何を参照しているかがわかります。
ポールドレーパー

回答:


45

nullがメソッドの適切な入力パラメーターである場合、メソッドを修正します。そうでない場合は、呼び出し元を修正します。「合理的」は柔軟な用語なので、次のテストを提案します。メソッドはどのようにヌル入力を渡すべきですか?考えられる答えが複数見つかった場合、nullは妥当な入力ではありません


1
それは本当にそれと同じくらい簡単です。
-biziclop

3
グアバには、nullチェックをできるだけ簡単にするいくつかの非常に便利なヘルパーメソッドがありますPrecondition.checkNotNull(...)stackoverflow.com/questions/3022319/…–

私のルールは、可能な場合はすべてを適切なデフォルトに初期化し、その後ヌル例外を心配することです。
davidk01

Thorbjørn:偶然のNullPointerExceptionを意図的なNullPointerExceptionに置き換えますか?場合によっては、早期チェックを実行することが有用であるか、必要な場合さえあります。しかし、付加価値がほとんどなく、プログラムを大きくするだけの無意味なボイラープレートnullチェックがたくさんあるのではないかと心配しています。
-user281377

6
nullがメソッドへの妥当な入力パラメーターではないが、メソッドの呼び出しが誤ってnullを渡す可能性が高い場合(特にメソッドがパブリックの場合)、IllegalArgumentExceptionnullが指定されたときにメソッドをスローすることもできます。これは、メソッドの呼び出し側に、バグが(メソッド自体ではなく)コードにあることを通知します。
ブライアン

20

nullを使用しないで、Optionalを使用します

あなたが指摘したように、nullJava での最大の問題の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 Guavaをご覧ください。

グアバチームによる適切な説明: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値を使用して実行できる賢明な方法がない場合は、コードの50%をnullにチェックしないでください。

一方、null使用できて何か意味がある場合は、必ず使用してください。


最終的に、明らかにnullJavaから削除することはできません。Optional可能な限り抽象化を代用し、nullそれについて合理的なことができる他の時間をチェックすることを強くお勧めします。


2
通常、4年前の質問に対する回答は、品質が低いため削除されてしまいます。Java 8(当時は存在していなかった)がどのように問題を解決できるかについてお話ししました。

もちろん、元の質問とは無関係です。そして、引数が実際にオプションであるとはどういうことですか?
15

5
@jwenting元の質問に完全に関連しています。「ドライバーで釘を打つ最良の方法は何ですか」に対する答えは、「ドライバーではなくハンマーを使用する」です。
デニス

@jwenting、私はそれをカバーしたと思いますが、明確にするために:引数はオプションではないので、私は通常nullをチェックしません。100行のヌルチェックと40行の実体があります。より多くのパブリックインターフェイスでnullをチェックするか、nullが実際に意味をなす場合(つまり、引数はオプションです)。
ポールドレーパー

4
Javaで、あなたはどのようになり、J-ボス、@ ない未チェックを持っていますかNullPointerException?A NullPointerExceptionは、Javaでインスタンスメソッドを呼び出すたびに文字通り発生 します。あなたは持っているでしょうthrows NullPointerExceptionこれまでにほぼすべての単一の方法で。
ポールドレイパー

8

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

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

nullとは

すべての定義とケースで、Nullはデータの絶対的な不足を表します。データベースのヌルは、その列の値がないことを表します。理論的には、null はStringemptyと同じではなく、null はZEROと同じものではありません。実際には「依存する」。Empty は、ビジネスロジックに依存するため、Stringクラスの適切な実装を作成できます。StringintStringNull ObjectInteger

代替手段は次のとおりです。

  1. Null Objectパターン。null状態を表すオブジェクトのインスタンスを作成し、その型へのすべての参照をNull実装への参照で初期化します。また、これは可能性が他のオブジェクトへの参照をたくさん持っていないシンプルな値型のオブジェクトのために有用であるnullとことが期待されnull、有効な状態とします。

  2. アスペクト指向ツールを使用してNull Checker、パラメーターがnullにならないようにアスペクトを備えたメソッドを作成します。これはnull、エラーが発生した場合に使用します。

  3. 使用するのassert()if (obj != null){}ノイズよりも少ないが、ノイズは少ない。

  4. Contracts For Javaなどの契約施行ツールを使用します。AspectJのようなものと同じユースケースですが、より新しいものであり、外部構成ファイルの代わりに注釈を使用します。アスペクトとアサートの両方の作品のベスト。

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

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

最終的には

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


可能であれば、nullオブジェクトパターンが、nullケースを処理する最も優雅な方法です。nullを使用して実稼働環境でデータを爆発させることはまれです!
リチャードミスキン

@Richard:それnullが予想外でない場合、それは真のエラーである場合、すべてが完全に停止するはずです。

プログラミングでは、:データベースで、コードのプログラミングでのNULLは、一つの重要な局面では異なる方法で処理されnull == null、(でもPHPで)が、データベース内null != nullのデータベースにそれが表しているため、未知の値ではなく、「何を」。2つの未知数は必ずしも等しいとは限りませんが、2つの何も等しくありません。
サイモンフォースバーグ

8

nullチェックを追加すると、テストに問題が生じる可能性があります。この素晴らしい講義をご覧ください...

Google Tech talksの講義をご覧ください:「The Clean Code Talks-Do n't Look For Things!」彼は24分頃にそれについて話します

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

妄想プログラミングでは、あらゆる場所にヌルチェックを追加します。最初は良いアイデアのように見えますが、テストの観点からは、nullチェックタイプのテストを扱いにくくしています。

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

class House(Door door){

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

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

モック作成地獄で苦しんでいる人は、この種の迷惑をよく知っています。

要約すると、テストスイートは、ドア、家、屋根など、それについて妄想することなくテストできるほど堅牢でなければなりません。Seroiusly、テストの特定のオブジェクトにヌルチェックテストを追加するのはどれほど難しいですか:)

動作することを証明するいくつかのテストがあるため、動作するアプリケーションを常に優先する必要があります。


4

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

詳細

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

ルール#1:

nullパラメーターを許可するかnull値を返すメソッドのjavadocは、これを明確に文書化し、その意味を説明する必要があります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)、ある種のアプリケーションは、おそらくプログラミングエラーを検出した後に続行しようとする必要があります。パフォーマンスへの影響。)


3

防御的な方法に修正することをお勧めします。例えば:

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

これに沿ったものになるはずです:

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

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


10
これには、呼び出し側(このAPIに対してコーディングするプログラマー)が「デフ​​ォルト」動作を取得するためのパラメーターとしてnullを渡す習慣を開発するという厄介な副作用があります。
ゴランジョヴィック

2
これがJavaの場合new String()、まったく使用しないでください。
biziclop

1
@biziclopは実際、ほとんどの人が知っているよりもはるかに重要です。
Woot4Moo

1
私の意見では、引数がnullの場合は、呼び出し側の間違いであるため、アサートして例外をスローする方が理にかなっています。発信者の間違いを黙って「修正」しないでください。代わりに、それらを認識させます。
アンドレスF.

1
@ Woot4Mooしたがって、例外をスローする独自のコードを作成します。重要なことは、渡された引数が間違っていることを呼び出し側に通知し、できるだけ早くそれらを伝えることです。nullsをサイレントに修正することは、NPEをスローすることよりも悪い最悪のオプションです。
アンドレスF.

2

これまでのところ、nullに関する次の一般的なルールは、私を大いに助けてくれました。

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

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

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

  4. おそらく最も重要なのは...テスト、テスト、そして再びテスト。コードをテストするときは、コードとしてテストしないでください。ナチのサイコパスの支配者としてテストし、そのコードから地獄を拷問するあらゆる種類の方法を想像してみてください。

これは、システムが外部の世界とインターフェイスし、冗長性が豊富な内部の値を厳密に制御するファサードやプロキシーにつながることが多いヌルに関して、偏執的な側面に少しありがちです。ここの外の世界は、私が自分でコーディングしなかったほとんどすべてのものを意味します。実行時間はかかりますが、これまでのところ、コードの「null安全なセクション」を作成して最適化する必要はほとんどありませんでした。私は主にヘルスケアのために長時間稼働するシステムを作成し、最後にしたいことは、他のシステムの他の誰かがその名前がアポストロフィまたはただし耒耨のような文字を含む。

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


1

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

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

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

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


0

あなたのメソッドの可能な結果の1つがnull値である場合、あなたはそのために防御的にコーディングする必要がありますが、メソッドがnull以外を返すはずですが、私は確かにそれを修正しません。

いつものように、人生のほとんどのものと同様に、場合によって異なります:)



0
  1. 本当にオプションである場合を除き、オプションとして使用しないでください。多くの場合、定期的にバグのあるコードを作成するためではなく、オプションとして本当にnullを想定している場合を除き、出口は例外として適切に処理されます。

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

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

  4. 例外処理には、オプションのようなタイプとは異なり、JVMで異なる操作の優先順位と特権が許可されます。例外の発生の例外的な性質に意味があり、メモリへのハンドラーの優先的な遅延読み込みの早期サポートを含む常にぶらぶらしている必要があります。

  5. nullハンドラーをあちこちに記述する必要はありません。発生する可能性がある場所、信頼できないデータサービスにアクセスする可能性のある場所のみ、これらのほとんどは簡単に抽象化できるいくつかの一般的なパターンに該当するため、本当に必要なのはまれな場合を除き、ハンドラー呼び出し。

したがって、私の答えは、チェックされた例外でラップして処理するか、信頼できないコードを修正できると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.