Java: ヌルポインタを効率的にチェックする方法 質問する

Java: ヌルポインタを効率的にチェックする方法 質問する

メソッドのパラメータにnull値が与えられているかどうかを確認するためのパターンがいくつかあります。

まず、古典的なものです。これは自作のコードでは一般的であり、理解するのは明らかです。

public void method1(String arg) {
  if (arg == null) {
    throw new NullPointerException("arg");
  }
}

2つ目は、既存のフレームワークを使うことです。このコードは1行で済むので見た目が少し良くなります。欠点は、別のメソッドを呼び出す可能性があることです。かもしれないコンパイラによっては、コードの実行速度が少し遅くなります。

public void method2(String arg) {
  Assert.notNull(arg, "arg");
}

3 番目に、オブジェクトに副作用を与えずにメソッドを呼び出そうとすることができます。これは最初は奇妙に見えるかもしれませんが、上記のバージョンよりもトークンが少なくなります。

public void method3(String arg) {
  arg.getClass();
}

3 番目のパターンが広く使用されているのを見たことはありませんが、まるで自分で発明したかのような気がします。このパターンは短いため、またコンパイラが最適化によって完全に除去するか、単一のマシン命令に変換できる可能性が高いため気に入っています。また、私は行番号情報を使用してコードをコンパイルしているので、 a がNullPointerExceptionスローされた場合、行ごとにそのようなチェックが 1 つしかないため、正確な変数までさかのぼることができます。

どちらのチェックが好みですか、またその理由は何ですか?

ベストアンサー1

アプローチ3: arg.getClass();は賢いやり方ですが、この慣用句が広く採用されない限り、私は文字数を節約するよりも、より明確で冗長な方法の方を好みます。私は「一度書いて何度も読む」タイプのプログラマーです。

他のアプローチは自己文書化されています。何が起こったかを明確にするために使用できるログ メッセージがあります。このログ メッセージは、コードを読むときと実行時に使用されます。arg.getClass()現状では、自己文書化されていません。少なくとも、コードのレビュー担当者に明確にするためにコメントを使用できます。

arg.getClass(); // null check

しかし、他の方法のように、ランタイムに特定のメッセージを入れる機会はまだありません。


アプローチ #1 と #2 (null チェック + NPE/IAE と assert):私は次のようなガイドラインに従うようにしています:

http://data.opengeo.org/GEOT-290810-1755-708.pdf

  • assertプライベートメソッドのパラメータをチェックするために使用します
    assert param > 0;

  • IllegalArgumentExceptionパブリックメソッドのパラメータをチェックするには、null チェック + を使用します。
    if (param == null) throw new IllegalArgumentException("param cannot be null");

  • 必要に応じてnullチェック+NullPointerExceptionを使用する
    if (getChild() == null) throw new NullPointerException("node must have children");


しかしこの質問は潜在的な問題を最も効率的に捕捉することに関するものかもしれないので、私が好んで扱う方法は静的解析、例えば型注釈(例)を使うことだnullと言わざるを得ない。null@NonNullJSR-305これらを確認するための私のお気に入りのツールは次のとおりです。

Checker Framework:
Java 用のカスタム プラグ可能な型
https://checkerframework.org/manual/#checker-guarantees

それが私のプロジェクト(たとえば、パブリック API を持つライブラリではない)であり、Checker Framework を全体的に使用できる場合:

  • API で自分の意図をより明確に文書化できます (例: このパラメーターは null (デフォルト) ではない可能性がありますが、このパラメーターは null ( ) である可能性があります@Nullable。メソッドは null を返す可能性があります。など)。この注釈は、Javadoc 内の離れた場所ではなく、宣言のすぐ近くにあるため、維持される可能性がはるかに高くなります。

  • 静的解析は実行時チェックよりも効率的である

  • 静的分析では、実行時に発生する問題に依存するのではなく、潜在的なロジックの欠陥 (たとえば、null 以外のパラメーターのみを受け入れるメソッドに null の可能性がある変数を渡そうとしたなど) を事前にフラグ付けします。

もう1つの利点は、このツールを使用すると、注釈をコメントに記入できることです(例:`/@Null可能/) なので、私のライブラリ コードは、型注釈付きのプロジェクトと型注釈なしのプロジェクト (私はどちらも持っていませんが) と互換性があります。


リンクが再び切れた場合以下は GeoTools 開発者ガイドからのセクションです。

http://data.opengeo.org/GEOT-290810-1755-708.pdf

5.1.7 アサーション、IllegalArgumentException、NPE の使用

Java 言語では、数年前から assert キーワードが利用可能になっています。このキーワードは、デバッグのみのチェックを実行するために使用できます。この機能にはさまざまな用途がありますが、一般的な用途は、プライベート (パブリックではない) メソッドのメソッド パラメータをチェックすることです。その他の用途としては、事後条件と不変条件があります。

参照: アサーションを使ったプログラミング

事前条件 (プライベート メソッドの引数チェックなど) は、通常、アサーションの簡単なターゲットです。事後条件と不変条件は、それほど単純ではありませんが、より価値があります。これは、重要な条件は破られるリスクが高いためです。

  • 例1:参照モジュールでのマップ投影の後、アサーションは逆マップ投影を実行し、結果を元のポイントでチェックします (事後条件)。
  • 例2:DirectPosition.equals(Object) 実装では、結果が true の場合、アサーションによって hashCode() が Object 契約で要求されているとおりに同一であることが保証されます。

アサートを使用してプライベートメソッドのパラメータをチェックする

private double scale( int scaleDenominator ){
 assert scaleDenominator > 0;
 return 1 / (double) scaleDenominator;
}

次のコマンドラインパラメータを使用してアサーションを有効にすることができます。

java -ea MyApp

次のコマンドライン パラメータを使用して、GeoTools アサーションのみをオンにすることができます。

java -ea:org.geotools MyApp

次に示すように、特定のパッケージのアサーションを無効にすることができます。

java -ea:org.geotools -da:org.geotools.referencing MyApp

IllegalArgumentExceptions を使用してパブリック メソッドのパラメータをチェックする

パブリック メソッドでのアサートの使用は絶対にお勧めしません。報告されている間違いはクライアント コードで発生したものであるためです。クライアントが失敗した場合は、正直に IllegalArgumentException を使用して前もってクライアントに伝えてください。

public double toScale( int scaleDenominator ){
 if( scaleDenominator > 0 ){
 throw new IllegalArgumentException( "scaleDenominator must be greater than 0");
 }
 return 1 / (double) scaleDenominator;
}

必要に応じてNullPointerExceptionを使用する

可能であれば、独自の null チェックを実行し、何が問題であったかについての詳細な情報とともに IllegalArgumentException または NullPointerException をスローします。

public double toScale( Integer scaleDenominator ){
 if( scaleDenominator == null ){
 throw new NullPointerException( "scaleDenominator must be provided");
 }
 if( scaleDenominator > 0 ){
 throw new IllegalArgumentException( "scaleDenominator must be greater than 0");
 }
 return 1 / (double) scaleDenominator;
}

おすすめ記事