28

次のような呼び出しで NullPointerException を取得した場合:

someObject.getSomething().getSomethingElse().
    getAnotherThing().getYetAnotherObject().getValue();

次のようなかなり役に立たない例外テキストが表示されます。

Exception in thread "main" java.lang.NullPointerException
at package.SomeClass.someMethod(SomeClass.java:12)

どの呼び出しが実際に null を返したかを見つけるのはかなり難しく、コードを次のようにリファクタリングしていることがよくあります。

Foo ret1 = someObject.getSomething();
Bar ret2 = ret1.getSomethingElse();
Baz ret3 = ret2.getAnotherThing();
Bam ret4 = ret3.getYetAnotherOject();
int ret5 = ret4.getValue();

そして、どの行を探すべきかを教えてくれるよりわかりやすい NullPointerException を待ちます。

ゲッターを連結するのは悪いスタイルであり、とにかく避けるべきだと主張する人もいるかもしれませんが、私の質問は次のとおりです。コードを変更せずにバグを見つけることはできますか?

ヒント: 私は Eclipse を使用しており、デバッガーが何であるかは知っていますが、それを問題に適用する方法がわかりません。

回答に対する私の結論:
いくつかの回答は、ゲッターを次々とチェーンするべきではないと教えてくれました。いくつかの回答は、そのアドバイスに従わなかった場合にコードをデバッグする方法を示しました。

いつゲッターをチェーンするかを正確に教えてくれた答えを受け入れました:

  • null を返すことができない場合は、好きなだけ連鎖させます。!= null をチェックする必要はありません。NullPointerExceptions について心配する必要はありません (連鎖は依然としてデメテルの法則に違反していることに注意してください。しかし、私はそれを受け入れることができます) 。
  • それらが null を返す可能性がある場合は、絶対に連鎖させず、null を返す可能性のあるそれぞれについて null 値のチェックを実行

これにより、実際のデバッグに関する適切なアドバイスは役に立たなくなります。

4

11 に答える 11

16

NPEは、Javaで最も役に立たない例外です。これは常に怠惰に実装されているようで、「class xyZ is null」のように単純な場合でも、そのような場合のデバッグに大いに役立ちます。

とにかく、これらの場合にNPEスローアーを見つけるために私が見つけた唯一の良い方法は、次の種類のリファクタリングです。

someObject.getSomething()
          .getSomethingElse()
          .getAnotherThing()
          .getYetAnotherObject()
          .getValue();

これで、NPEは正しいラインを指し、したがって実際のNPEをスローした正しいメソッドを指します。私が望むほどエレガントなソリューションではありませんが、機能します。

于 2009-01-05T06:33:49.287 に答える
11

答えは、ゲッター (のコントラクト) をどのように見るかによって異なります。それらが返される可能性がある場合はnull、毎回戻り値を実際に確認する必要があります。ゲッターが を返すべきでない場合、ゲッターはnullチェックを含み、決して返さないと約束した をIllegalStateException返す代わりに例外 (?)をスローする必要があります。nullスタックトレースは、正確なゲッターを示します。ゲッターが見つけた予期しない状態を例外メッセージに入れることさえできます。

于 2009-01-04T12:33:05.287 に答える
9

IntelliJ IDEA では、exceptionbreakpointsを設定できます。これらのブレークポイントは、指定された例外がスローされるたびに発生します (これをパッケージまたはクラスにスコープできます)。

そうすれば、NPE の原因を簡単に見つけることができます。

ネットビーンズやエクリプスでも同様のことができると思います。

編集:これは、 Eclipseで例外ブレークポイントを追加する方法に関する説明です

于 2009-01-04T12:35:11.383 に答える
5

頻繁に書いていることに気付いた場合:

a.getB().getC().getD().getE();

これはおそらくコードの臭いであり、避ける必要があります。たとえば、a.getE()どの呼び出しにb.getE()どの呼び出しを呼び出すかをリファクタリングできc.getE()ますd.getE()。(この例は特定のユースケースには意味がないかもしれませんが、このコードの臭いを修正するための1つのパターンです。)

デメテルの法則も参照してください。

  • メソッドは、そのクラスの他のメソッドを直接呼び出すことができます
  • メソッドは、それ自体のフィールドでメソッドを直接呼び出すことができます(ただし、フィールドのフィールドでは呼び出すことはできません)。
  • メソッドがパラメーターを受け取る場合、メソッドはそれらのパラメーターのメソッドを直接呼び出すことができます。
  • メソッドがローカルオブジェクトを作成するとき、そのメソッドはローカルオブジェクトのメソッドを呼び出すことができます。

したがって、メッセージのチェーンを持つべきではありませんa.getB().getC().doSomething()。この「法則」に従うことには、NullPointerExceptionsのデバッグを容易にする以外にも多くの利点があります。

于 2009-01-04T17:27:34.117 に答える
3

通常、複数の null 許容ゲッターがある場合、このようなゲッターをチェーンしません。

IDE内で実行している場合は、ブレークポイントを設定して、各要素でIDEの「式の評価」機能を連続して使用できます。

しかし、運用サーバーのログからこのエラー メッセージが表示された瞬間、頭を悩ませることになるでしょう。そのため、1 行に最大 1 つの null 許容アイテムを保持することをお勧めします。

一方、 Groovy の安全なナビゲーション オペレーターを夢見ることができます。

于 2009-01-04T12:19:39.760 に答える
2

早期の失敗もオプションです。

null 値を返すことができるコード内の任意の場所で、null 戻り値のチェックを導入することを検討してください。

public Foo getSomething()
{
  Foo result;
  ...
  if (result == null) {
    throw new IllegalStateException("Something is missing");
  }
  return result;
}
于 2009-01-04T12:35:18.670 に答える
2

Eclipse を使用してバグを見つける方法を次に示します。

まず、行にブレークポイントを設定します。

someObject.getSomething().getSomethingElse().
getAnotherThing().getYetAnotherObject().getValue();

プログラムをデバッグ モードで実行し、行がヒットしたときにデバッガーがパースペクティブに切り替わるようにします。

ここで、「someObject」を強調表示して CTRL+SHIFT+I を押します (または右クリックして「inspect」と言います)。

ヌルですか?NPE が見つかりました。非nullですか?次に、someObject.getSomething() (かっこを含む) を強調表示して調べます。ヌルですか?コードを変更せずに、チェーンをたどって NPE が発生している場所を特定します。

于 2009-01-04T15:49:24.237 に答える
1

行を分割したり、問題を特定するために精巧なデバッグを行ったりする必要がある場合、それは一般に、コードがnullを十分に早くチェックしていないことを伝える神の方法です。

オブジェクトパラメータを受け取るメソッドまたはコンストラクタがあり、問題のオブジェクト/メソッドがそのパラメータがnullであることを適切に処理できない場合は、NullPointerExceptionをチェックしてスローします。

「1行に複数のドットを使用することは許可されていません」など、この問題を回避するために「コーディングスタイル」のルールを考案する人がいるのを見てきました。しかし、これはバグを間違った場所で見つけるプログラミングを奨励するだけです。

于 2009-01-04T20:07:27.947 に答える
1

!= null の回避に関するこの質問を参照することをお勧めします。

基本的に、null が有効な応答である場合は、それを確認する必要があります。そうでない場合は、それをアサートします (可能な場合)。ただし、何をするにしても、他の理由の中で null が有効な応答であるケースを最小限に抑えるようにしてください。

于 2009-01-04T12:31:28.693 に答える
0

各ゲッターをそれぞれの行に配置してデバッグします。各メソッドをステップ オーバー (F6) して、null を返す呼び出しを見つけます。

于 2009-01-04T17:07:52.363 に答える
0

このような連鎖式は、NullPointerExceptions (および発生する可能性のある他のほとんどの問題) のためにデバッグするのが面倒なので、回避することをお勧めします。おそらく十分に聞いたことがあると思いますが、以前の投稿者が言及したように、実際の NullPointerException にブレークポイントを追加して、発生した場所を確認できます。

Eclipse (およびほとんどの IDE) では、ウォッチ式を使用して、デバッガーで実行されているコードを評価することもできます。コードを選択してこれを行い、contet メニューを使用して新しい時計を追加します。

null を返すメソッドを制御している場合、null が返される有効な値である場合は、Null オブジェクト パターンを検討することもできます。

于 2009-01-04T14:36:22.310 に答える