Pertanyaan Anotasi Java @NotNull apa yang harus saya gunakan?


Saya ingin membuat kode saya lebih mudah dibaca serta menggunakan perkakas seperti inspeksi kode IDE dan / atau analisis kode statis (FindBugs dan Sonar) untuk menghindari NullPointerExceptions. Banyak alat yang tampaknya tidak sesuai satu sama lain. @NotNull/@NonNull/@Nonnull anotasi dan daftar semuanya dalam kode saya akan sangat buruk untuk dibaca. Ada saran yang mana yang 'terbaik'? Berikut adalah daftar anotasi setara yang saya temukan:

  • javax.validation.constraints.NotNull
    Dibuat untuk validasi waktu proses, bukan analisis statis.
    dokumentasi

  • edu.umd.cs.findbugs.annotations.NonNull
    Digunakan oleh Findbugs analisis statis dan karena itu Sonar (sekarang Sonarqube)
    dokumentasi

  • javax.annotation.Nonnull
    Ini mungkin bekerja dengan Findbugs juga, tapi JSR-305 tidak aktif. (Lihat juga: Bagaimana status JSR 305?) sumber

  • org.jetbrains.annotations.NotNull
    Digunakan oleh IDE IntelliJ IDE untuk analisis statis.
    dokumentasi

  • lombok.NonNull
    Digunakan untuk mengontrol pembuatan kode di Proyek Lombok.
    Anotasi placeholder karena tidak ada standar.
    sumber, dokumentasi

  • android.support.annotation.NonNull
    Anotasi penanda tersedia di Android, disediakan oleh paket dukungan-anotasi
    dokumentasi

  • org.eclipse.jdt.annotation.NonNull
    Digunakan oleh Eclipse untuk analisis kode statis
    dokumentasi


739
2018-02-10 22:05


asal


Jawaban:


Saya sangat menyukai Kerangka Pemeriksa, yang merupakan implementasi dari jenis anotasi (JSR-308) yang digunakan untuk mengimplementasikan pengecek cacat seperti pemeriksa nol. Saya belum benar-benar mencoba yang lain untuk menawarkan perbandingan apa pun, tetapi saya senang dengan penerapan ini.

Saya tidak berafiliasi dengan grup yang menawarkan perangkat lunak, tetapi saya adalah penggemar.

Empat hal yang saya suka tentang sistem ini:

  1. Ini memiliki catur cacat untuk nol (@Nullable), tetapi juga memiliki untuk kekekalan dan magang (dan lain-lain). Saya menggunakan yang pertama (nullness) dan saya mencoba untuk menggunakan yang kedua (immutability / IGJ). Saya mencoba yang ketiga, tapi saya belum yakin untuk menggunakannya dalam jangka panjang. Saya tidak yakin tentang kegunaan umum dari checker lain, tetapi bagus untuk mengetahui bahwa kerangka itu sendiri adalah sistem untuk menerapkan berbagai penjelasan tambahan dan checkers.

  2. Itu pengaturan standar untuk pemeriksaan nullness bekerja dengan baik: Non-null kecuali penduduk setempat (NNEL). Pada dasarnya ini berarti bahwa secara default pemeriksa memperlakukan everyhing (variabel instan, parameter metode, tipe generik, dll) kecuali variabel lokal seolah-olah mereka memiliki tipe @NonNull secara default. Per dokumentasi:

    Standar NNEL mengarah ke jumlah anotasi eksplisit terkecil dalam kode Anda.

    Anda dapat menetapkan standar yang berbeda untuk kelas atau metode jika NNEL tidak berfungsi untuk Anda.

  3. Kerangka ini memungkinkan Anda untuk menggunakannya tanpa menciptakan ketergantungan pada kerangka kerja dengan menyertakan anotasi Anda dalam komentar: mis. /*@Nullable*/. Ini bagus karena Anda dapat membubuhkan keterangan dan memeriksa pustaka atau kode bersama, tetapi masih dapat menggunakan pustaka itu / kode bersama dalam proyek lain yang tidak menggunakan kerangka kerja. Ini fitur yang bagus. Saya sudah terbiasa menggunakannya, meskipun saya cenderung mengaktifkan Kerangka Pemeriksa pada semua proyek saya sekarang.

  4. Kerangka ini memiliki cara untuk menganotasi API Anda menggunakan yang belum dianotasi untuk nullness dengan menggunakan file stub.


78
2018-02-10 22:59



Sejak Oracle memutuskan untuk tidak membakukan @NonNull (dan @Nullable) untuk saat ini, saya khawatir tidak ada jawaban yang bagus. Yang bisa kita lakukan adalah menemukan solusi pragmatis dan saya adalah sebagai berikut:

Sintaksis

Dari sudut pandang murni gaya saya ingin menghindari referensi ke IDE, kerangka kerja atau alat manapun kecuali Java itu sendiri.

Aturan ini keluar:

  • android.support.annotation
  • edu.umd.cs.findbugs.annotations
  • org.eclipse.jdt.annotation
  • org.jetbrains.annotations
  • org.checkerframework.checker.nullness.qual
  • lombok.NonNull

Yang meninggalkan kita dengan javax.validation.constraints atau javax.annotation. Yang pertama datang dengan JEE. Jika ini lebih baik daripada javax.annotation, yang mungkin akhirnya datang dengan JSE atau tidak pernah sama sekali, adalah masalah perdebatan. Saya pribadi lebih suka javax.annotation karena saya tidak suka ketergantungan JEE.

Ini membuat kita bersama

javax.annotation

yang juga merupakan yang terpendek.

Hanya ada satu sintaks yang bahkan akan lebih baik: java.annotation.Nullable. Saat paket lain lulus dari javax ke java di masa lalu, javax.annotation akan menjadi langkah ke arah yang benar.

Pelaksanaan

Saya berharap bahwa mereka semua pada dasarnya memiliki implementasi sepele yang sama, tetapi analisis rinci menunjukkan bahwa ini tidak benar.

Pertama untuk kesamaan:

The @NonNull penjelasan semua memiliki garis

public @interface NonNull {}

kecuali untuk

  • org.jetbrains.annotations yang menyebutnya @NotNull dan memiliki implementasi yang sepele
  • javax.annotation yang memiliki implementasi lebih lama
  • javax.validation.constraints yang juga memanggilnya @NotNull dan memiliki implementasi

The @Nullable annotations semua memiliki garis

public @interface Nullable {}

kecuali (lagi) org.jetbrains.annotations dengan implementasi sepele mereka.

Untuk perbedaan:

Yang mencolok adalah itu

  • javax.annotation
  • javax.validation.constraints
  • org.checkerframework.checker.nullness.qual

semua memiliki anotasi waktu proses (@Retention (RUNTIME), sementara

  • android.support.annotation
  • edu.umd.cs.findbugs.annotations
  • org.eclipse.jdt.annotation
  • org.jetbrains.annotations

hanya waktu kompilasi (@Retention (CLASS)).

Seperti yang dijelaskan dalam jawaban SO ini dampak anotasi waktu proses lebih kecil dari yang mungkin dipikirkan, tetapi mereka memiliki manfaat alat yang memungkinkan untuk melakukan pemeriksaan runtime selain kompilasi yang waktu.

Perbedaan penting lainnya adalah dimana dalam kode anotasi dapat digunakan. Ada dua pendekatan berbeda. Beberapa paket menggunakan konteks gaya JLS 9.6.4.1. Tabel berikut memberikan ikhtisar:


                                METODE FIELD PARAMETER LOCAL_VARIABLE
android.support.annotation X X X
edu.umd.cs.findbugs.annotations X X X X
org.jetbrains.annotation X X X X
lombok X X X X
javax.validation.constraints X X X

org.eclipse.jdt.annotation, javax.annotation dan org.checkerframework.checker.nullness.qual gunakan konteks yang ditentukan di JLS 4.11, yang menurut saya adalah cara yang tepat untuk melakukannya.

Ini membuat kita bersama

  • javax.annotation
  • org.checkerframework.checker.nullness.qual

di ronde ini.

Kode

Untuk membantu Anda membandingkan detail lebih lanjut, saya mencantumkan kode setiap anotasi di bawah ini. Untuk memudahkan perbandingan, saya menghapus komentar, impor, dan anotasi @Documented. (Mereka semua memiliki @Documented kecuali untuk kelas dari paket Android). Saya mengatur ulang garis dan bidang @Target dan menormalkan kualifikasi.

package android.support.annotation;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER})
public @interface NonNull {}

package edu.umd.cs.findbugs.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NonNull {}

package org.eclipse.jdt.annotation;
@Retention(CLASS)
@Target({ TYPE_USE })
public @interface NonNull {}

package org.jetbrains.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NotNull {String value() default "";}

package javax.annotation;
@TypeQualifier
@Retention(RUNTIME)
public @interface Nonnull {
    When when() default When.ALWAYS;
    static class Checker implements TypeQualifierValidator<Nonnull> {
        public When forConstantValue(Nonnull qualifierqualifierArgument,
                Object value) {
            if (value == null)
                return When.NEVER;
            return When.ALWAYS;
        }
    }
}

package org.checkerframework.checker.nullness.qual;
@Retention(RUNTIME)
@Target({TYPE_USE, TYPE_PARAMETER})
@SubtypeOf(MonotonicNonNull.class)
@ImplicitFor(
    types = {
        TypeKind.PACKAGE,
        TypeKind.INT,
        TypeKind.BOOLEAN,
        TypeKind.CHAR,
        TypeKind.DOUBLE,
        TypeKind.FLOAT,
        TypeKind.LONG,
        TypeKind.SHORT,
        TypeKind.BYTE
    },
    literals = {LiteralKind.STRING}
)
@DefaultQualifierInHierarchy
@DefaultFor({TypeUseLocation.EXCEPTION_PARAMETER})
@DefaultInUncheckedCodeFor({TypeUseLocation.PARAMETER, TypeUseLocation.LOWER_BOUND})
public @interface NonNull {}

Untuk kelengkapan, berikut adalah implementasi @Nullable:

package android.support.annotation;
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD})
public @interface Nullable {}

package edu.umd.cs.findbugs.annotations;
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
@Retention(CLASS)
public @interface Nullable {}

package org.eclipse.jdt.annotation;
@Retention(CLASS)
@Target({ TYPE_USE })
public @interface Nullable {}

package org.jetbrains.annotations;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface Nullable {String value() default "";}

package javax.annotation;
@TypeQualifierNickname
@Nonnull(when = When.UNKNOWN)
@Retention(RUNTIME)
public @interface Nullable {}

package org.checkerframework.checker.nullness.qual;
@Retention(RUNTIME)
@Target({TYPE_USE, TYPE_PARAMETER})
@SubtypeOf({})
@ImplicitFor(
    literals = {LiteralKind.NULL},
    typeNames = {java.lang.Void.class}
)
@DefaultInUncheckedCodeFor({TypeUseLocation.RETURN, TypeUseLocation.UPPER_BOUND})
public @interface Nullable {}

Dua paket berikut tidak memiliki @Nullable, jadi saya daftar secara terpisah lombok memiliki @NonNull yang cukup membosankan. Dalam javax.validation.constraints @NonNull sebenarnya adalah @NotNull dan itu memiliki implementasi yang lama.

package lombok;
@Retention(CLASS)
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE})
public @interface NonNull {}

package javax.validation.constraints;
@Retention(RUNTIME)
@Target({ FIELD, METHOD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Constraint(validatedBy = {})
public @interface NotNull {
    String message() default "{javax.validation.constraints.NotNull.message}";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default {};
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
    @Retention(RUNTIME)
    @Documented
    @interface List {
        NotNull[] value();
    }
}

Mendukung

Formulir pengalaman saya javax.annotation setidaknya didukung oleh Eclipse dan Checker Framework di luar kotak.

Ringkasan

Anotasi ideal saya adalah sintaks java.annotation dengan implementasi Checker Framework.

Jika Anda tidak berniat menggunakan Kerangka Checker javax.annotation (JSR-305) masih merupakan pilihan terbaik untuk saat ini.

Jika Anda bersedia membeli ke dalam Checker Framework, gunakan saja org.checkerframework.checker.nullness.qual mereka.


Sumber-sumber

  • android.support.annotation dari android-5.1.1_r1.jar
  • edu.umd.cs.findbugs.annotations dari findbugs-annotations-1.0.0.jar
  • org.eclipse.jdt.annotation dari org.eclipse.jdt.annotation_2.1.0.v20160418-1457.jar
  • org.jetbrains.annotations dari jetbrains-annotations-13.0.jar
  • javax.annotation dari gwt-dev-2.5.1-sources.jar
  • org.checkerframework.checker.nullness.qual dari checker-framework-2.1.9.zip
  • lombok dari lombok commit f6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
  • javax.validation.constraints dari validation-api-1.0.0.GA-sources.jar

60
2018-03-09 12:20



Saya menggunakan IntelliJ satu, karena saya sangat prihatin dengan hal-hal penandaan IntelliJ yang mungkin menghasilkan NPE. Saya setuju bahwa itu membuat frustrasi karena tidak memiliki penjelasan standar di JDK. Ada pembicaraan untuk menambahkannya, mungkin membuatnya menjadi Java 7. Dalam hal ini akan ada satu lagi yang bisa dipilih!


49
2018-02-10 22:09



Menurut Daftar fitur Java 7 Anotasi tipe JSR-308 ditunda ke Java 8. Keterangan JSR-305 bahkan tidak disebutkan.

Ada sedikit info tentang keadaan JSR-305 dalam lampiran draft JSR-308 terbaru. Ini termasuk pengamatan bahwa anotasi JSR-305 tampaknya ditinggalkan. Halaman JSR-305 juga menunjukkannya sebagai "tidak aktif".

Sementara itu, jawaban pragmatis adalah menggunakan jenis anotasi yang didukung oleh alat yang paling banyak digunakan ... dan siap untuk mengubahnya jika situasinya berubah.


Faktanya, JSR-308 tidak mendefinisikan jenis / kelas anotasi apa pun, dan sepertinya mereka menganggapnya di luar jangkauan. (Dan mereka benar, mengingat keberadaan JSR-305).

Namun, jika JSR-308 benar-benar terlihat seperti masuk ke Java 8, itu tidak akan mengejutkan saya jika minat di JSR-305 dihidupkan kembali. AFAIK, tim JSR-305 belum secara resmi meninggalkan pekerjaan mereka. Mereka baru saja diam selama 2 tahun lebih.

Sangat menarik bahwa Bill Pugh (pemimpin teknologi untuk JSR-305) adalah salah satu orang di belakang FindBugs.


31
2018-02-10 22:21



Untuk proyek Android yang harus Anda gunakan android.support.annotation.NonNull dan android.support.annotation.Nullable. Anotasi spesifik Android khusus dan bermanfaat ini tersedia di Dukungan Perpustakaan.

Dari http://tools.android.com/tech-docs/support-annotations:

Perpustakaan dukungan itu sendiri juga telah dianotasi dengan ini   anotasi, sehingga sebagai pengguna pustaka dukungan, Android Studio akan   sudah memeriksa kode Anda dan menandai potensi masalah berdasarkan ini   anotasi.


25
2017-09-05 09:51



JSR305 dan FindBugs ditulis oleh orang yang sama. Keduanya tidak dirawat dengan baik tetapi standar seperti yang didapat dan didukung oleh semua IDE utama. Kabar baiknya adalah bahwa mereka bekerja dengan baik apa adanya.

Berikut adalah cara menerapkan @Nonnull ke semua kelas, metode, dan bidang secara default. Lihat https://stackoverflow.com/a/13319541/14731 dan https://stackoverflow.com/a/9256595/14731

  1. Menetapkan @NotNullByDefault
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;


    /**
     * This annotation can be applied to a package, class or method to indicate that the class fields,
     * method return types and parameters in that element are not null by default unless there is: <ul>
     * <li>An explicit nullness annotation <li>The method overrides a method in a superclass (in which
     * case the annotation of the corresponding parameter in the superclass applies) <li> there is a
     * default parameter annotation applied to a more tightly nested element. </ul>
     * <p/>
     * @see https://stackoverflow.com/a/9256595/14731
     */
    @Documented
    @Nonnull
    @TypeQualifierDefault(
    {
        ElementType.ANNOTATION_TYPE,
        ElementType.CONSTRUCTOR,
        ElementType.FIELD,
        ElementType.LOCAL_VARIABLE,
        ElementType.METHOD,
        ElementType.PACKAGE,
        ElementType.PARAMETER,
        ElementType.TYPE
    })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface NotNullByDefault
    {
    }

2. Tambahkan anotasi ke setiap paket: package-info.java

@NotNullByDefault
package com.example.foo;

MEMPERBARUI: Mulai 12 Desember 2012 JSR 305 terdaftar sebagai "Tidak Aktif". Menurut dokumentasi:

JSR yang dipilih sebagai "tidak aktif" oleh Komite Eksekutif, atau yang telah mencapai akhir masa hidup alami.

Sepertinya JSR 308  aku s membuatnya menjadi JDK 8 dan meskipun JSR tidak mendefinisikan @NotNull, yang menyertainya Checkers Framework tidak. Pada saat penulisan ini, plugin Maven tidak dapat digunakan karena bug ini: https://github.com/typetools/checker-framework/issues/183


16
2017-11-23 02:52



Jika ada yang hanya mencari kelas IntelliJ: Anda bisa mendapatkannya dari repositori maven dengan

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations</artifactId>
    <version>15.0</version>
</dependency> 

16
2018-04-25 14:15



Eclipse juga memiliki anotasi tersendiri.

org.eclipse.jdt.annotation.NonNull

Lihat di http://wiki.eclipse.org/JDT_Core/Null_Analysis untuk detailnya.


11
2017-09-17 11:10



Hanya menunjukkan bahwa Java Validation API (javax.validation.constraints.*) tidak datang dengan @Nullable anotasi, yang sangat berharga dalam konteks analisis statis. Masuk akal untuk validasi bean runtime karena ini adalah default untuk setiap bidang non-primitif di Java (yaitu tidak ada yang validasi / menegakkan). Untuk tujuan menyatakan bahwa harus mempertimbangkan alternatif.


10
2017-08-01 12:07