ゲッターのチェーンでNullPointerExceptionをトレースする方法
-
03-07-2019 - |
質問
次のような呼び出しで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値のチェックを実行してください
これにより、実際のデバッグに関する有益なアドバイスが役に立たなくなります。
解決
答えは、ゲッターの表示(契約)によって異なります。それらが null
を返す可能性がある場合は、毎回実際に戻り値を確認する必要があります。ゲッターが null
を返さない場合、ゲッターはチェックを含み、 null
を返す代わりに例外( IllegalStateException
?)をスローする必要があります。二度と戻らないと約束した。スタックトレースは、正確なゲッターを指し示します。ゲッターが検出した予期しない状態を例外メッセージに含めることもできます。
他のヒント
NPEは、Javaで最も役に立たない期間です。 「class x.y.Z is null」のように単純な場合でも、常に遅延実装されており、それが原因を正確に伝えないようです。そのような場合のデバッグに大いに役立ちます。
とにかく、これらの場合にNPEスローを見つける唯一の良い方法は、次の種類のリファクタリングです。
someObject.getSomething()
.getSomethingElse()
.getAnotherThing()
.getYetAnotherObject()
.getValue();
これで、NPEが正しい行を指し示し、実際のNPEを投げた正しい方法を示します。私が望んでいるほどエレガントなソリューションではありませんが、機能します。
IntelliJ IDEAでは、 exceptionbreakpoints を設定できます。これらのブレークポイントは、指定された例外がスローされるたびに起動します(これをパッケージまたはクラスにスコープできます)。
その方法で、NPEのソースを簡単に見つけることができます。
netbeansまたはeclipseでも同様のことができると思います。
編集:こちらは、Eclipseで例外ブレークポイントを追加する方法の説明です
頻繁に書いていることに気付いた場合:
a.getB().getC().getD().getE();
これはおそらくコードの匂いであり、避けるべきです。たとえば、 a.getE()
をリファクタリングして、 b.getE()
を呼び出し、 c.getE()
を呼び出して、 d.getE()
。 (この例は特定のユースケースには意味がないかもしれませんが、このコードの匂いを修正するための1つのパターンです。)
デメテルの法則も参照してください。
- メソッドは、そのクラスの他のメソッドを直接呼び出すことができます
- メソッドは独自のフィールドでメソッドを直接呼び出すことができます(フィールドのフィールドではありません)
- メソッドがパラメーターを受け取る場合、メソッドはそれらのパラメーターのメソッドを直接呼び出すことができます。
- メソッドがローカルオブジェクトを作成すると、そのメソッドはローカルオブジェクトのメソッドを呼び出すことができます。
したがって、メッセージのチェーンを持ってはいけません。 a.getB()。getC()。doSomething()
。この「法律」に従ってNullPointerExceptionsのデバッグを簡単にすること以外にも多くの利点があります。
私は通常、複数のnull可能ゲッターがある場合、このようなゲッターをチェーンしません。
IDE内で実行している場合は、ブレークポイントを設定して、「式を評価」を使用できます。各要素に対するIDEの機能を連続して実行します。
しかし、本番サーバーのログからこのエラーメッセージを受け取った瞬間に頭を悩ますことになるでしょう。したがって、1行につき最大1つのNULL入力可能アイテムを保持するのが最適です。
初期障害もオプションです。
コード内でnull値が返される可能性がある場所では、null戻り値のチェックを導入することを検討してください。
public Foo getSomething()
{
Foo result;
...
if (result == null) {
throw new IllegalStateException("Something is missing");
}
return result;
}
Eclipseを使用してバグを見つける方法。
最初に、行にブレークポイントを設定します:
someObject.getSomething().getSomethingElse().
getAnotherThing().getYetAnotherObject().getValue();
プログラムをデバッグモードで実行し、デバッガーが行にヒットしたときにパースペクティブに切り替えられるようにします。
今、「someObject」をハイライトします; Ctrl + Shift + Iキーを押します(または右クリックして「検査」と言います)。
それはヌルですか? NPEが見つかりました。 null以外ですか? 次に、someObject.getSomething()(括弧を含む)を強調表示して検査します。 nullですか?コードを変更することなく、NPEが発生している場所を把握するためにチェーンを続けます。
回避についてのこの質問を参照することもできます!= null 。
基本的に、nullが有効な応答である場合、それを確認する必要があります。そうでない場合は、それをアサートします(可能な場合)。しかし、あなたが何をするにしても、他の理由の中でも特にnullが有効な応答であるケースを最小限に抑えてください。
問題を特定するために行を分割したり、詳細なデバッグを行ったりする必要がある場合、それは通常、コードが十分に早くnullをチェックしていないことを伝える神の方法です。
オブジェクトパラメーターを受け取るメソッドまたはコンストラクターがあり、問題のオブジェクト/メソッドがそのパラメーターをnullに適切に処理できない場合は、NullPointerExceptionをチェックしてスローします。
「コーディングスタイル」を発明する人々を見てきました「1行に複数のドットを許可しない」など、この問題を回避しようとするルール。しかし、これは間違った場所でバグを発見するプログラミングを促進するだけです。
そのような連鎖式は、NullPointerExceptions(および発生する可能性のある他のほとんどの問題)のデバッグに苦痛を伴うため、試して回避することをお勧めします。ただし、前述のポスターで述べたように、実際のNullPointerExceptionにブレークポイントを追加して、どこで発生したかを確認できることをおそらく聞いたことがあるでしょう。
Eclipse(およびほとんどのIDE)では、監視式を使用して、デバッガーで実行中のコードを評価することもできます。コードを選択してこの操作を行い、contetメニューを使用して新しいウォッチを追加します。
nullを返すメソッドを制御している場合、nullが返す有効な値である場合は、Null Objectパターンも考慮することができます。
各ゲッターを独自の行に配置してデバッグします。各メソッドをステップオーバー(F6)して、どの呼び出しがnullを返すかを見つけます