@Nullableおよび@Nonnullアノテーションをより効果的に使用するにはどうすればよいですか?


140

私はそれを見ることができる@Nullable@Nonnull注釈ができ予防に有用であるNullPointerExceptionのを、彼らは非常に遠くに伝播されません。

  • これらの注釈の有効性は、1レベルの間接参照の後に完全に低下するため、ほんの数個追加しただけでは、あまり伝播しません。
  • これらの注釈は十分に実施されていないため、でマークされた値@Nonnullがnull ではないと想定し、その結果nullチェックを実行しない危険があります。

以下のコードはでマークされたパラメータが発生@Nonnullするnullいかなる苦情を上昇させずに。NullPointerException実行されるとそれをスローします。

public class Clazz {
    public static void main(String[] args){
        Clazz clazz = new Clazz();

        // this line raises a complaint with the IDE (IntelliJ 11)
        clazz.directPathToA(null);

        // this line does not
        clazz.indirectPathToA(null); 
    }

    public void indirectPathToA(Integer y){
        directPathToA(y);
    }

    public void directPathToA(@Nonnull Integer x){
        x.toString(); // do stuff to x        
    }
}

これらの注釈をより厳密に適用および/またはさらに伝播させる方法はありますか?


1
私の考えのような@Nullableまたは@Nonnull彼らは価値がある場合、それは「要請議論に可能性が」非常にある
マールテンBodewes

これによりコンパイラエラーまたは警告が発生する世界に移動@Nonnullする@Nonnull方法は、null許容変数を持つメソッドを呼び出すときにキャストを要求することだと思います。もちろん、アノテーションを使用したキャストはJava 7では不可能ですが、Java 8では、キャストを含む変数の使用にアノテーションを適用する機能が追加されます。これは、Java 8に実装することが可能となるので
セオドア・マードック

1
@TheodoreMurdockはい、Java 8ではキャスト(@NonNull Integer) yは構文的に可能ですが、コンパイラーは注釈に基づいて特定のバイトコードを発行することはできません。ランタイムアサーションの場合、bugs.eclipse.org / 442103で説明されているように、小さなヘルパーメソッドで十分です(例:)directPathToA(assertNonNull(y))。安全な唯一の方法は、実際のnullチェックを実行することです(うまくいけば、elseブランチの代替実装)。
Stephan Herrmann、2015

1
それは言ってこの質問に参考になる@Nonnull@Nullable複数の類似annoationsがあるので(参照してください、あなたが話しているこの質問を)。パッケージ内の注釈について話していますjavax.annotationか?
James Dunn、

1
@TJamesBooneこの質問のコンテキストでは問題ではありませんが、これはそれらのいずれかを効果的に使用する方法に関するものでした。
マイク・ライランダー2017

回答:


66

短い答え:これらのアノテーションは、IDEが潜在的にnullポインターエラーを警告する場合にのみ役立つと思います。

"クリーンコード"ブックで述べたように、パブリックメソッドのパラメーターをチェックし、不変条件のチェックも回避する必要があります。

別の良いヒントは、null値を返すことはありませんが、代わりにnullオブジェクトパターンを使用することです。


10
空の戻り値の可能性があるためOptional、プレーンではなくタイプを使用することを強くお勧めしますnull
Patrick

7
オプションは「null」よりも優れていません。Optional#get()はNoSuchElementExceptionをスローし、nullを使用するとNullPointerExceptionをスローします。どちらもRuntimeExceptionのものであり、意味のある説明はありません。私はnull可能変数を好みます。
18

4
@ 30thhなぜ最初にOptional.isPresent()やOptional.mapではなく、直接Optional.get()を使用するのですか?
GauravJ 2018

7
@GauravJ最初にnullかどうかを確認せずに、null許容変数を直接使用するのはなぜですか。;-)
2018

5
Optionalこの場合のnull可能との違いは、Optionalこの値を意図的に空にすることができることをよりよく伝えていることです。確かに、これは魔法の杖ではなく、ランタイムでは、null許容変数とまったく同じように失敗する可能性があります。ただし、Optional私の意見では、プログラマーによるAPIの受信の方が優れています。
user1053510

31

null引数がnullでないことを期待するメソッドに渡すときにヒントを提供するIDE以外に、次のような利点があります。

  • 静的コード分析ツールはIDEと同じようにテストできます(例:FindBugs)
  • AOPを使用してこれらのアサーションを確認できます

これにより、コードの保守性が向上し(nullチェックが不要になるため)、エラーが発生しにくくなります。


9
ここでOPに共感します。これら2つの利点を引用していますが、どちらの場合も「できる」という言葉を使用したためです。つまり、これらのチェックが実際に行われる保証はありません。さて、この動作の違いは、本番モードでの実行を避けたいパフォーマンス重視のテストに役立ちますassert。私は見つける@Nullable@Nonnull便利なアイデアであることを、私は、私たちは1が何についての仮定ではなく、その背後にもっと力をたい可能性がまだ葉は彼らと何もしないの可能性を開いている、彼らと一緒に行います。
12

2
問題はどこから始めるかです。現時点では、彼の話は任意です。時々私はそれがそうでなかったらそれを望みます。なぜならいくつかの状況ではそれらを強制することは有用だからです...
Uwe Plonus

ここであなたが言及しているAOPは何ですか?
Chris.Zou、2015年

@ Chris.Zou AOPは、アスペクト指向プログラミングを意味します。たとえば、AspectJ
Uwe Plonus

13

この元の質問は、@ NonNullが使用されていても、実行時のnullポインターチェックが引き続き必要であるという一般的な推奨事項を間接的に指摘していると思います。次のリンクを参照してください。

Java 8の新しい型注釈

上記のブログでは、次のことをお勧めします。

オプションの型注釈はランタイム検証の代わりにはなりません。型注釈の 前は、null可能性や範囲などを記述するための主要な場所はjavadocでした。Typeアノテーションを使用すると、この通信はコンパイル時の検証方法でバイトコードに入ります。コードは引き続きランタイム検証を実行する必要があります。


1
理解されていますが、デフォルトのlintチェックでは、実行時のnullチェックが不要であることを警告します。最初の印象では、この推奨事項は推奨されないようです。
スウビー2017年

1
@swoobyコードが正しいと確信している場合は、通常、lintの警告を無視します。これらの警告はエラーではありません。
jonathanzh 2017年

12

Eclipseでコンプライアンス1.8の元の例をコンパイルし、アノテーションベースのnull分析を有効にすると、次の警告が表示されます。

    directPathToA(y);
                  ^
Null type safety (type annotations): The expression of type 'Integer' needs unchecked conversion to conform to '@NonNull Integer'

この警告は、生成されたコードを生の型を使用してレガシーコードと混合するときに発生する警告( "チェックされていない変換")と同様に表現されています。ここでもまったく同じ状況です。メソッドindirectPathToA()には「レガシー」シグネチャがあり、nullコントラクトを指定していません。ツールはこれを簡単に報告できるので、nullアノテーションを伝播する必要があるが、まだ伝播されていないすべての路地を追いかけます。

そして、賢いもの@NonNullByDefaultを使うとき、毎回これを言う必要すらありません。

言い換えると、nullアノテーションが「遠くまで伝播する」かどうかは、使用するツール、およびツールによって発行されたすべての警告にどれほど厳密に対応するかに依存します。TYPE_USEヌル注釈あなたは最終的にツールがについて警告聞かせするオプションが持っているすべての nullであるかどうかは、型システムのintrisic財産となっていますので、あなたのプログラムで可能なNPEを。


8

注釈は「あまり遠くまで伝播しない」ことに同意します。しかし、プログラマー側には間違いがあるようです。

Nonnull注釈をドキュメントとして理解しています。次のメソッドは、(前提条件として)null以外の引数が必要であることを表していますx

    public void directPathToA(@Nonnull Integer x){
        x.toString(); // do stuff to x        
    }

次のコードスニペットにはバグが含まれています。メソッドは、それがnullでdirectPathToA()ないことを強制せずに呼び出しyます(つまり、呼び出されたメソッドの前提条件は保証されません)。1つの可能性は、(前提条件の伝播)にNonnullも注釈を追加することindirectPathToA()です。可能性2は、yin がnull かどうかを確認し、when がnullの場合indirectPathToA()の呼び出しを回避することです。directPathToA()y

    public void indirectPathToA(Integer y){
        directPathToA(y);
    }

1
伝播@Nonnull 持っていることはindirectPathToA(@Nonnull Integer y)私見は悪い習慣です:あなたは(あなたが追加した場合ので、完全なコールスタック上の伝播をmainainする必要がありますnullにチェックをdirectPathToA()、あなたは交換する必要があります@Nonnullによって@Nullable、完全なコールスタックで)。これは、大規模なアプリケーションの場合、多大なメンテナンス作業になります。
ジュリアンクロネッグ

@Nonnullアノテーションは、引数のnull検証がユーザー側にあることを強調しているだけです(null以外の値を渡すことを保証する必要があります)。メソッドの責任ではありません。
Alexander Drobyshevsky

@Nonnullは、この方法でnull値が意味をなさない場合にも賢明です
Alexander Drobyshevsky

5

私のプロジェクトで行うことは、「定数条件と例外」コードインスペクションで次のオプションをアクティブ
にすることです。nullを返す可能性のあるメソッドに@Nullableアノテーションを提案し、アノテーションなしのパラメーターに渡されたnull可能値を報告する 検査

有効にすると、すべての非注釈付きパラメーターが非nullとして扱われるため、間接呼び出しに関する警告も表示されます。

clazz.indirectPathToA(null); 

さらに強力なチェックを行うには、Checker Frameworkを選択することをお勧めします(この素晴らしいチュートリアルを参照してください
:まだ使用していないため、Jackコンパイラに問題がある可能性があります。このバグレポートを参照してください)


4

Javaでは、GuavaのOptionalタイプを使用します。実際の型であるため、コンパイラはその使用について保証します。これをバイパスしてを取得するのは簡単ですNullPointerExceptionが、少なくともメソッドのシグネチャは、引数として期待されるもの、またはメソッドが返すものを明確に伝えます。


16
これには注意が必要です。オプションは、値が本当にオプションである場合にのみ使用してください。オプションがない場合は、以降のロジックの決定ゲートとして使用されます。私はこれを、オプションのオブジェクトの全面的な置き換えとヌルチェックが、要点を逃した存在のチェックで置き換えられることで悪用されるのを見てきました。
クリストファーペリー

JDK 8以降をターゲットにしている場合はjava.util.Optional、Guavaのクラスの代わりに使用することをお勧めします。違いの詳細については、グアバのメモ/比較を参照してください。
AndrewF

1
あなたはポイントは何で、それから、手の込んだことができ、「ヌルチェックがポイントミスの存在をチェックしに置き換え」ですが?私の意見では、これがオプションの唯一の理由ではありませんが、間違いなく最大かつ最良の理由です。
スキューボ

4

Kotlinを使用する場合、コンパイラでこれらのnull可能性アノテーションがサポートされ、null以外の引数を必要とするjavaメソッドにnullを渡すことができなくなります。この質問は元々Javaを対象としたものでしたが、これらのJavaアノテーションを特に対象としているため、このKotlin機能について言及しました。これらのアノテーションをより厳密に適用および/またはさらに伝播させる方法はありますか?」この機能により、これらの注釈がより厳密に適用されます。

@NotNullアノテーションを使用したJavaクラス

public class MyJavaClazz {
    public void foo(@NotNull String myString) {
        // will result in an NPE if myString is null
        myString.hashCode();
    }
}

KotlinクラスがJavaクラスを呼び出し、@ NotNullで注釈が付けられた引数にnullを渡す

class MyKotlinClazz {
    fun foo() {
        MyJavaClazz().foo(null)
    }
}  

@NotNullアノテーションを強制するKotlinコンパイラエラー。

Error:(5, 27) Kotlin: Null can not be a value of a non-null type String

参照:http : //kotlinlang.org/docs/reference/java-interop.html#nullability-annotations


3
この質問は、最初のタグに従ってJavaを対象としており、Kotlinについては対象外です。
seh

1
@sehこの回答がこの質問に関連する理由の更新を参照してください。
Mike Rylander

2
けっこうだ。これはKotlinの優れた機能です。Javaについて学ぶためにここに来る人々を満足させるとは思えません。
seh

ただし、パラメータにが追加されていなくmyString.hashCode()ても、アクセスするとNPEがスロー@NotNullされます。それを追加することについてより具体的には何ですか?
kAmol 2018年

@kAmolここでの違いは、Kotlinを使用すると、ランタイムエラーではなくコンパイル時エラーが発生することです。アノテーションは、開発者がnullが渡されないようにする必要があることを通知するためのものです。これにより、実行時にnullが渡されるのを防ぐことはできませんが、このメソッドをnullで呼び出すコードを作成できなくなります(またはnullを返すことができる関数を使用)。
Mike Rylander、

-4

Java 8の新機能はオプションであるため、独自のコードで@Nullableまたは@Notnullを使用しないでください。以下の例を見てください:

public void printValue(@Nullable myValue) {
    if (myValue != null) {
        System.out.print(myValue);
    } else {
        System.out.print("I dont have a value");
}

それは次のように書き換えることができます:

public void printValue(Optional<String> myValue) {
    if (myValue.ifPresent) {
        System.out.print(myValue.get());
    } else {
        System.out.print("I dont have a value");
}

オプションを使用すると、null値をチェックする必要があります。上記のコードでは、getメソッドを呼び出すことによってのみ値にアクセスできます。

別の利点は、コードが読みやすくなることです。Java 9 ifPresentOrElseを追加すると、関数は次のように書くこともできます。

public void printValue(Optional<String> myValue) {
    myValue.ifPresentOrElse(
        v -> System.out.print(v),
        () -> System.out.print("I dont have a value"),
    )
}

を使用しても、Optionalこれらのアノテーションを使用する多くのライブラリとフレームワークが存在するため、すべての依存関係をオプションを使用するように更新されたバージョンに更新/置換することはできません。Optionalただし、独自のコード内でnullを使用する状況では役立ちます。
Mike Rylander
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.