takeda_san’s blog

KotlinとVRを頑張っていく方向。

🍥Nonnullって、何ですか?

コトの発端

Java 11の新機能ってどんなんだろなと、眺めていたらこんなコードがありまして。

(@Nonnull var x, @Nullable var y) -> x.process(y)

このコードはJEP 323: Local-Variable Syntax for Lambda Parametersの中で例として出てるやつ。
内容は、『varがラムダ式の仮変数(って呼び方あってます?)にも付けられるようになるよ!』ってことなんだけど、その前に@Nonnullって、何ですか?
当たり前のように出てきてますが、私はご存じないアノテーションです。

nullじゃない三銃士

さっそく@Nonnullで調べてたら、色んな種類の"nullじゃない"が出てきてちょっと混乱してきたので整理します。
nullじゃない三銃士を連れてきたよ。
(細かく調べると三銃士どころじゃなく、もっといると思います)

1. Bean Validationの@NotNull

Java EEとかSpringとかで、よく見かける"nullじゃない"。
JavaBeansのメンバ変数につけて、その変数に入る値がnullの時に入力エラーとするためのアノテーション。

こんな感じ。
実際に値が入ってバリデーションが実行されて、入力値がエラーかどうかわかる。

public class ImageModel {
    @NotNull
    private int imageId;
    private String imageFileName;

getterとかsetterとか以下略
}

詳しくはコチラが詳しい・わかりやすいなのでどうぞ。
JavaEE使い方メモ(Bean Validation)

2. Lombokの@NonNull

黒魔術でおなじみのLombokの"nullじゃない"。
メソッドの引数につけて、その引数がnullのときにNullPointerExceptionが投げられる。
よくあるnullチェック、

if (param == null) throw new NullPointerException("param");

と等価。

こんな感じ。
print()を呼び出そうとするとぬるぽで落ちる。

private void print() {
    printName(null);
}

private void printName(@NonNull String name) {
    System.out.println(name);
}

Java 7以降はObjects.requireNonNull(hoge)でいいんじゃなかなぁという気はする。

3. 静的解析に使う@Notnull

これが今回知りたかった"nullじゃない"。
SpotBugsやIDEでの静的解析に利用される。
例えばメソッドの引数に@Notnullをつけることで、引数にnullが指定されたときにIDEがエラーとして指摘してくれる。
アプリケーションの動作には関係なく、バグの検証に使われる。
(nullを入れるな!と明示することによってメソッドの仕様を表すのにも使うのかな…)

こんな感じ。
printDateTime()のthis.setInputDateTime(null);のところでIDEがエラーを指摘してくれる。
setInputDateTimeメソッドの引数の指定に@NonNullを指定したので、IDEが気を使ってくれたのです。
ありがとう、Eclipse、ありがとう。

public class Tsurami {

    private LocalDateTime inputDateTime;

    public void printDateTime() {
        this.setInputDateTime(null);
        System.out.println(this.getInputDateTime());
    }

    public LocalDateTime getInputDateTime() {
        return inputDateTime;
    }

    public void setInputDateTime(@NonNull LocalDateTime inputDateTime) {
        this.inputDateTime = inputDateTime;
    }
}

f:id:takeda_san:20180530224553p:plain

沢山あるぞ!静的解析に使うnullじゃない

この便利な静的解析に使うnullじゃないですが、同じような機能のライブラリがいくつかあります。
お好みでどうぞ!という感じがありますが、多少なりとも違いがあります。
このstackoverflowの質問が程よくまとまっていて分かりやすいので、解説を書くまでもないのですが一応…

eclipse - Which @NonNull Java annotation to use - Stack Overflow

javax.annotation.Nonnull

FindBugsから提供されているjsr305-x.x.x.jarの@Nonnull。
googleのライブラリを使うと、一緒についてくることが多い。
名前の通り、JSR305の実装となる予定だったっぽい。
The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 305

悲しいことにJSR305はちょっとお休み中のようなので、しばらくはこのまま外部ライブラリのままでしょうね。
そもそも、FindBugs自体がSpotBugsに移行しましたし、移行先のSpotBugsでもjsr305.jarファイルはリリースされないようです。
(よく見たらSpecification Leadsの人がFindBugsのプロジェクトリーダの人と同一人物?)

http://spotbugs.readthedocs.io/ja/latest/migration.html#com-google-code-findbugs-jsr305

org.eclipse.jdt.annotation.NonNull

Eclipseが提供している@NonNull。
(nullの頭文字が大文字だ!)
Eclipseで使ったときに、アノテーションを利用した静的解析ができる。
ほかのIDEでも、使えそうな気はするけどJavaDocにも"Eclipseでコンパイルしたとき"ってわざわざ書いてあるし避けたほうがいいかも。

Help - Eclipse Platform

org.jetbrains.annotations.NotNull

JetBrainsが提供している@NotNull。
(Notだ!)
IntelliJ IDEAで使ったときに、アノテーションを利用した静的解析ができる。
IntelliJ IDEAのアルティメーット!!!を持ってないので、どんな使い心地なのかわかりません… アルティメーット!!!ほしい。

android.support.annotation.NonNull

android studio用(雑)

Checker Frameworkの@NonNull

オープンソースのその名の通り、チェッカーフレームワーク。
特定のIDE用というわけではないので、SpotBugsとかで解析したいときはこれなのかな。

型のアノテーションへの対応

色々ライブラリがあるのはわかったけど、型アノテーションへの対応はどうなの?
型アノテーションって何というと、こんなのである。

String hoge = (@NonNull String) "piyo";
List<@NonNull String> piyo  = new ArrayList<>();

実際のプロダクト用のコードでこれを発見すると、見慣れなさに若干ひるむやつである。
試した限り、org.eclipse.jdt.annotationとChecker Frameworkは対応しているようだ。
ナウでヤングな書き方をしたい方はどちらかを選ぶと幸せになれる気がする。

付録:Eclipseで@Nonnullしよう

今回、Eclipseでいろいろと動作を確認していたのですが、デフォルトだとエラー指摘してくれなかったので簡単に手順を書いておく。
(画像を一通り保存してから気づいたけどこれSTSだ)

EclipseのツールバーからWindow -> Preferences Errors/WarningsのメニューからNull analysisを開いてEnable annotation ~~をチェック。
f:id:takeda_san:20180530233710p:plain

org.eclipse.jdt.annotation以外を使うときは、Configureリンクから対応するアノテーションのクラスを変更する。
f:id:takeda_san:20180530234058p:plain

f:id:takeda_san:20180530234105p:plain