最近、私の同僚がいくつかのコードを書いて、メソッド全体のnullポインタ例外をキャッチし、単一の結果を返しました。 nullポインターの理由がいくつあったかを指摘したので、1つの結果の防御チェックに変更しました。
しかし、NullPointerExceptionをキャッチすることは、私には間違っているように思えました。私の考えでは、ヌルポインター例外は不良コードの結果であり、システムで予期される例外ではありません。
Nullポインタ例外をキャッチすることが理にかなっているケースはありますか?
はい、RuntimeException
をキャッチすることは、ほとんどの場合コードのにおいです。 C2 Wiki は同意するようです。
例外はおそらく、他のモジュールからかなりランダムなコードを実行する特別に防御的なコードです。このような防御構造の例は、 [〜#〜] edt [〜#〜] 、ThreadPools/Executors、およびプラグインシステムです。
NullPointerException
をキャッチするための1つの使用法を考えることができます。
catch (NullPointerException) {
ApplyPainfulElectricShockToProgrammer();
}
サードパートライブラリのバグのため、ヌルポインタ例外をキャッチする必要がありました。私たちが使用したライブラリーはその例外を投げました、そしてそれは私たちがそれについて何もできませんでした。
その場合、それをキャッチするのは[〜#〜] ok [〜#〜]であり、それ以外の場合はキャッチしません。
場合によります。
この同僚はどの程度の経験がありますか?彼は無知/怠惰のためにこれをしているのですか、それとも本当に正当な理由がありますか? (これは何よりもメインスレッドであり、決して死ぬべきではありませんか?)
ランタイム例外をキャッチする時間の90%が間違っており、NullPointerExceptionをキャッチする99%が間違っています(理由が"私はそれらの多くを取得していた..."の場合、プログラマー全体が間違っており、あなたは彼がやっている残りのコードに注意する必要があります)
ただし、状況によっては、NullPointerExceptionのキャッチが許容される場合があります。
おかしい
私は仕事でしてはいけないことを見つけました:
public static boolean isValidDate(final String stringDateValue) {
String exp = "^[0-9]{2}/[0-9]{2}/[0-9]{4}$";
boolean isValid = false;
try {
if (Pattern.matches(exp, stringDateValue)) {
String[] dateArray = stringDateValue.split("/");
if (dateArray.length == 3) {
GregorianCalendar gregorianCalendar = new GregorianCalendar();
int annee = new Integer(dateArray[2]).intValue();
int mois = new Integer(dateArray[1]).intValue();
int jour = new Integer(dateArray[0]).intValue();
gregorianCalendar = new GregorianCalendar(annee, mois - 1,
jour);
gregorianCalendar.setLenient(false);
gregorianCalendar.get(GregorianCalendar.YEAR);
gregorianCalendar.get(GregorianCalendar.MONTH);
gregorianCalendar.get(GregorianCalendar.DAY_OF_MONTH);
isValid = true;
}
}
} catch (Exception e) {
isValid = false;
}
return isValid;
}
バード:)
開発者はカレンダーにこの種の例外を発生させたいと考えていました。
Java.lang.IllegalArgumentException: DAY_OF_MONTH
at Java.util.GregorianCalendar.computeTime(GregorianCalendar.Java:2316)
at Java.util.Calendar.updateTime(Calendar.Java:2260)
at Java.util.Calendar.complete(Calendar.Java:1305)
at Java.util.Calendar.get(Calendar.Java:1088)
値を無効にする...
はい、それは機能しますが、それは本当に良い習慣ではありません...
例外を発生させる(特にスタックトレースを埋める)には、例外なくデータを手動でチェックするだけではありません...
一般的に、それはコードのにおいだと思います。防御チェックの方が良いように思えます。レポート/ログ記録のすべてのエラーをキャッチしたいイベントループなどを除いて、ほとんどの未チェックの例外をカバーするように拡張します。
私が考えることができる例外は、変更できないライブラリへの呼び出しに関するものであり、予防的なチェックが困難なアサーションの失敗に応答してnullポインタ例外を生成する可能性があります。
それは悪いですが、最適化されたバイトコードを生成する可能性があります。
ほとんどの場合、整数i
がnull
ではない場合、チェックにより全体的なパフォーマンスが低下します。小切手自体には、3つの命令(0〜4)が必要です。その後、ケース全体で7つの命令(0-14)がかかります。
public class IfNotNull {
public Integer i;
public String getIAsString() {
if (i != null) {
return i.toString();
} else {
return "";
}
}
}
public Java.lang.String getIAsString();
Code:
0: aload_0
1: getfield #2 // Field i:Ljava/lang/Integer;
4: ifnull 15
7: aload_0
8: getfield #2 // Field i:Ljava/lang/Integer;
11: invokevirtual #3 // Method Java/lang/Integer.toString:()Ljava/lang/String;
14: areturn // <- here we go
15: ldc #4 // String
17: areturn
次の [〜#〜] eafp [〜#〜] Python世界で一般的なアプローチです。null
のケースはコストがかかりますが、 not null
の場合、4つの命令(0〜7)のみが必要です。
public class TryCatch {
public Integer i;
public String getIAsString() {
try {
return i.toString();
} catch (NullPointerException npe) {
return "";
}
}
}
public Java.lang.String getIAsString();
Code:
0: aload_0
1: getfield #2 // Field i:Ljava/lang/Integer;
4: invokevirtual #3 // Method Java/lang/Integer.toString:()Ljava/lang/String;
7: areturn // <- here we go
8: astore_1
9: ldc #5 // String a
11: areturn
Exception table:
from to target type
0 7 8 Class Java/lang/NullPointerException
JITコンパイラがこれを最適化できるかどうかは誰にもわかりません。
プログラムが完全にクラッシュせずに回復できるように、NullPointerException(具体的には、任意のThrowable)をキャッチする唯一の場所は、トップレベルまたはシステム境界です。たとえば、web.xmlでエラーページを設定すると、キャッチオールが提供され、Webアプリケーションが例外から回復してユーザーに通知できるようになります。
確かにそうです。
ほとんどの場合、そもそも変数はnullであってはなりません。多くの新しい言語は、null不可の参照型(つまり、nullになることは決してないことが保証されている型)の組み込みサポートを備えています。
入力値をnullにできる場合は、チェックを行う必要があります。しかし、例外は間違いなくこれを行うには悪い方法です。
Ifステートメントは、実行するためにおそらく3つの指示を受け取り、ローカルチェックです(つまり、保証が必要なのと同じ場所でチェックを行います)。
一方、例外を使用すると、さらに多くの指示が必要になる場合があります。システムはメソッドの検索を試み、失敗し、例外テーブルを調べて適切な例外ハンドラーを探し、そこにジャンプして、ハンドラーを実行し、再度ジャンプします。さらに、チェックは潜在的に非ローカルです。コードが次のような場合:
try
return contacts.find("Mom").getEmail()
catch (NullPointerException e)
return null
NPEが「getEmail」でスローされたか「find」でスローされたかはわかりません。
より難読化された方法で記述された非常に一般的なパターンに対する技術的に悪い解決策?ランクではありませんが、悪臭がします:/
NULLポインタ例外のキャッチは、実際にはコンテキストに依存します...厳密な絶対ルールを回避するように努力する必要があります...ルールはコンテキストに適用する必要があります-この例外をトラップして、ソフトウェア全体を安定状態にしたい-何もしないか、ほとんどしない何もない。そのようなすべてのコーディング規則をよく理解する必要があります
この時点で、ソフトウェアAUDIT TRACE ...を見て、この例外の原因を発見します。
NULLポインタ例外が発生しないという考えは、検証可能でなければなりません。最初に静的分析を行い(サードパーティのコード/コンポーネントが入ってくると難しい)、次に関連ツールを使用して徹底的な状態空間検索を行います。
バツ
これはどうですか:
try
{
foo.x = bar;
}
catch (NullPointerException e)
{
// do whatever needs to be done
}
fooがnullの可能性がある場合のマイクロ最適化として、しかしほとんど決してそうではありませんか?
アイデアはこれです:
明示的なNULLチェックは単一の機械語命令を取ります
一方、2番目のバージョンのNULLチェックは、NULLアクセスを発生させ、SIGSEGVをキャッチし、NullPointerExceptionをスローすることで実行できます。オブジェクトがNULLでない場合、これは無料です。
ずっと前に、私には1つの用途がありました。特に愚かなライブラリは、コレクション内のオブジェクトをキーで要求され、そのオブジェクトが見つからなかった場合にNullPointerExceptionをスローします。キー以外に検索する方法はなく、オブジェクトが存在するかどうかを確認する方法もありませんでした。
しばらくして、ベンダーを起動し、ライブラリの変更を開始しました。これで、ライブラリはより良い例外(私の変更)をスローし、チェック関数(他の誰かの変更)を持ちます。
もちろん、私は常にtryブロック内にちょうど1行で終わることになります。これ以上、私自身も悪いコードの罪を犯します。
Swing-GUIベースのアプリケーションを完全に終了するには、NPE(実際にはすべてのRTE)をキャッチする必要がある場合があります。
編集:この場合、通常はUncaughtExceptionHandlerを介して行われます。
はいJavaの場合、NullPointerExceptionを確認する必要があります。
オブジェクトが必要な場合にアプリケーションがnullを使用しようとするとスローされます。これらには以下が含まれます:
Nullオブジェクトのインスタンスメソッドを呼び出す。 nullオブジェクトのフィールドへのアクセスまたは変更。 nullの長さを配列のように受け取ります。配列であるかのようにnullのスロットにアクセスまたは変更する。 Throwable値であるかのようにnullをスローします。
アプリケーションは、このクラスのインスタンスをスローして、nullオブジェクトの他の不正な使用を示す必要があります。
他の言語でテキストファイル(XMLなど)を読み取るときにNullPointerExceptionが発生し、そのレコードが正しいASCII文字とレコード形式に検証されていない)。
プログラマーが初心者である場合、すべての例外をキャッチする癖があり、最終的な出力が得られなくなります。これはコードレビューアが楽しまないでください。
RuntimeExceptionをキャッチすることは悪いことです。しかし、本当に必要な場合は、コード内のコメントは、そのコードに取り組む将来のプログラマーにとって非常に役立ちます。それらをキャッチするための合理的なコメントを書くことができない場合は、それらを回避する必要があります。限目。
それは本当にインターフェースの定義に依存します。非構造化NPEの処理は、例外またはスロー可能オブジェクトをキャッチするのと同じくらい良くありません。
Nullは、空の文字列やmax_intなどを使用するのではなく、初期化されていない状態を識別するのに役立ちます。かつてnullを定期的に使用していた場所は、コールバックオブジェクトが関係しない場所にあります。
Guiceが提供する@Nullableアノテーションが本当に気に入っています。
http://code.google.com/docreader/#p=google-guice&s=google-guice&t=UseNullable
コードベースのNullPointerExceptionsを排除するには、null参照についての規律が必要です。シンプルなルールに従い、実施することで、これに成功しています。
明示的に指定されていない限り、すべてのパラメータはnull以外です。 GoogleコレクションライブラリとJSR-305には、nullを制御するためのシンプルなAPIがあります。 Preconditions.checkNotNullは、null参照が見つかった場合の高速失敗に使用でき、@ Nullableを使用して、null値を許可するパラメーターに注釈を付けることができます。
Guiceはデフォルトでnullを禁止します。 nullの挿入を拒否し、代わりにProvisionExceptionで失敗します。クラスでnullが許可されている場合は、@ Nullableを使用してフィールドまたはパラメーターに注釈を付けることができます。 Guiceは、edu.umd.cs.findbugs.annotations.Nullableやjavax.annotation.Nullableなどの@Nullableアノテーションを認識します。
メソッドが外部インターフェイス(またはSOAP API))を呼び出し、返される値がNullである可能性がある場合、NullPointerExceptionをキャッチすると便利です。それ以外は、巨大ではありません。これらの例外をキャッチするメリット。
私は自分のインターフェースからの結果を保証しようとしますが、一部のライブラリーまたは誰かのコードが結果としてnullを生成する可能性があり、それを確実にキャッチすることを期待している場合は、実行可能かもしれません。もちろん、いったん捕まったら何をするかはあなた次第です。 nullをチェックしても意味がない場合もあり、それを見つけた場合、問題を解決するために他の方法を使用することもできます。
私が言っていることは、あなたができることの例外を使用することです、それはかなり良い言語機能です。