Intellij Ideaを使用してJava 6および7でNPEヘルを倒す

免責事項







JSR-305による救助



ここでは、ほぼ完全にNPEフリーのコードを正常に作成するのに役立つ、使用しているプラ​​クティスを共有したいと思います。 その主なアイデアは、JSR-305(com.google.code.findbugs:jsr305:1.3.9)を実装するライブラリのオプション値に関する注釈を使用することです。





当然、両方の注釈は、オブジェクトとクラスのフィールド、メソッドの引数と戻り値、ローカル変数に適用できます。 したがって、これらの注釈は、値の必須の存在に関して型情報を補完します。



ただし、すべてに長い時間注釈を付けると、コードの可読性が大幅に低下します。 したがって、原則として、プロジェクトチームは@Nullable



とマークされていないものはすべて必須であるという同意を受け入れます。 Guava、Guiceを使用した人は、このプラクティスをよく知っています。



以下に、このような抽象的なプロジェクトの可能なコードの例を示します。



 import javax.annotation.Nullable; public abstract class CodeSample { public void correctCode() { @Nullable User foundUser = findUserByName("vasya"); if(foundUser == null) { System.out.println("User not found"); return; } String fullName = Asserts.notNull(foundUser.getFullName()); System.out.println(fullName.length()); } public abstract @Nullable User findUserByName(String userName); private static class User { private String name; private @Nullable String fullName; public User(String name, @Nullable String fullName) { this.name = name; this.fullName = fullName; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Nullable public String getFullName() { return fullName; } public void setFullName(@Nullable String fullName) { this.fullName = fullName; } } }
      
      





どこでも見ることができるように、参照参照でnullを取得できるかどうかは明らかです。



唯一の注意点は、現在のコンテキスト(たとえば、ビジネスプロセスの特定の段階)で、一般的な場合に何かが存在しなければならないことが確実にわかっているときに状況が発生することです。 私たちの場合、これはVasilyのフルネームであり、原則としてユーザーが利用できない場合がありますが、ビジネスロジックのルールに従ってここでは不可能であることを知っています。 このような状況では、単純なアサートユーティリティを使用します。



 import javax.annotation.Nullable; public class Asserts { /** * For situations, when we definitely know that optional value cannot be null in current context. */ public static <T> T notNull(@Nullable T obj) { if(obj == null) { throw new IllegalStateException(); } return obj; } }
      
      





実際のJavaアサートも使用できますが、ランタイムに明示的に含める必要があり、構文があまり便利ではないため、慣れていませんでした。



継承と共分散/反分散に関するいくつかの言葉:





実際、これですでに十分であり、(IDEまたはCIでの)静的分析は特に必要ありません。 しかし、IDEを機能させてください。彼らがそれを買ったのは、何の理由でもありません。 私はIntellij Ideaを使用することを好むので、それ以降のすべての例はその上にあります。



Intellij Ideaは人生をより良くします



デフォルトでは、Ideaはアノテーションを同様のセマンティクスで提供しますが、他のすべての人を理解しています。 これは、[設定]-> [検査]-> [起こりそうなバグ]-> {一定の条件と例外; @NotNull/@Nullable



問題}。 両方の検査で、使用する注釈のペアを選択します。



以下は、前のコードの誤った実装での検査で見つかったエラーを強調表示した場合のIdeaの外観です。





IDEは非常に素晴らしいものになりました。IDEは2つのNPEを検出するだけでなく、それらを使用して何かを強制的に実行します。



すべて問題ないように見えましたが、Ideaビルトイン静的アナライザーは、受け入れたデフォルトのバインディングアグリーメントを理解していません。 彼女の観点から(および他の統計アナライザーと同様に)3つのオプションがここに表示されます。



そして、マークアップしなかったものはすべて不明と見なされます。 これは問題ですか? この質問に答えるには、NullableおよびNotNullのIdeaインスペクションが何を見つけることができるかを理解する必要があります。



これらの注釈でマークアップされていないライブラリメソッドから返される値は不明であるということは論理的です。 これに対処するには、割り当てるローカル変数またはフィールドに注釈を付けます。



引き続き慣行に従っている場合、コードではすべてがNullableオプションとしてマークされます。 したがって、最初のテストは引き続き機能し、多くのNPEから保護されます。 残念ながら、他のすべてのチェックは落ちました。 2番目のチェックは、引数として積極的にnullを受け入れ、このために設計されていない他の人のメソッドにnullを渡すメソッドを書くのが非常に好きな仲間に対して非常に役立ちますが、機能しません。



2番目のチェックの動作を復元するには、2つの方法があります。



どちらの場合も、デフォルトでは注釈のないメソッド引数のみがNotNullになります。 フィールド、ローカル変数、戻り値は影響を受けません。



近い将来



Intellij Idea 14 EAPで既に機能する@TypeQualifierDefaultの今後のサポートは、状況を改善するために呼び出されます。 それらを使用して、 @NonNullByDefault



アノテーションを定義できますこれにより、すべてのデフォルトバインディングが決定され、同じスコープがサポートされます。 現在、再帰もありませんが、議論があります。



以下は、注釈付きの新しいスタイルのコードを使用してレガシーコードから作業する3つのケースを検査がどのように探すかを示しています。



明示的に注釈を付けます:







デフォルトでは、引数のみ:







デフォルトでは、すべて:







終わり



すべてがほぼ素晴らしくなりました。IntellijIdea 14のリリースを待つ必要があります。完全に幸福になるまでまだ足りないのは、Java 8より前にGenericで型に注釈を付ける機能と、Type注釈のサポートです。 場合によっては、ListenableFuturesおよびコレクションの非常に不足しているもの。



記事の量が非常に重要であることが判明したため、ほとんどの例は船外に残っていましたが、 ここで入手できます



更新しました。 外部ライブラリのオプション値に関するメタ情報追加することは依然として可能です。



使用したソース



  1. stackoverflow.com/questions/16938241/is-there-a-nonnullbydefault-annotation-in-idea
  2. www.jetbrains.com/idea/webhelp/annotating-source-code.html
  3. youtrack.jetbrains.com/issue/IDEA-65566
  4. youtrack.jetbrains.com/issue/IDEA-125281



All Articles