Sejak JSR 305 (yang tujuannya adalah untuk membakukan @NonNulldan @Nullable) telah aktif selama beberapa tahun, saya khawatir tidak ada jawaban yang baik. Yang bisa kita lakukan adalah menemukan solusi pragmatis dan milikku adalah sebagai berikut:
Sintaksis
Dari sudut pandang gaya murni, saya ingin menghindari referensi ke IDE, framework atau toolkit apa pun kecuali Java itu sendiri.
Ini mengesampingkan:
android.support.annotation
edu.umd.cs.findbugs.annotations
org.eclipse.jdt.annotation
org.jetbrains.annotations
org.checkerframework.checker.nullness.qual
lombok.NonNull
Yang membuat kita salah javax.validation.constraintsatau javax.annotation. Yang pertama dilengkapi dengan JEE. Jika ini lebih baik daripada javax.annotation, yang mungkin datang bersama BEJ atau tidak pernah sama sekali, adalah masalah perdebatan. Saya pribadi lebih suka javax.annotationkarena saya tidak suka ketergantungan JEE.
Ini meninggalkan kita
javax.annotation
yang juga merupakan yang terpendek.
Hanya ada satu sintaks yang bahkan akan lebih baik: java.annotation.Nullable. Ketika paket-paket lain lulus dari javaxke javamasa lalu, javax.annotation akan menjadi langkah ke arah yang benar.
Penerapan
Saya berharap bahwa mereka semua pada dasarnya memiliki implementasi sepele yang sama, tetapi analisis rinci menunjukkan bahwa ini tidak benar.
Pertama untuk kesamaan:
Semua @NonNullanotasi memiliki garis
public @interface NonNull {}
kecuali untuk
org.jetbrains.annotationsyang menyebutnya @NotNulldan memiliki implementasi yang sepele
javax.annotation yang memiliki implementasi lebih lama
javax.validation.constraintsyang juga menyebutnya @NotNulldan memiliki implementasi
Semua @Nullableanotasi memiliki garis
public @interface Nullable {}
kecuali untuk (lagi) org.jetbrains.annotationsdengan implementasi sepele mereka.
Untuk perbedaan:
Yang mengejutkan adalah itu
javax.annotation
javax.validation.constraints
org.checkerframework.checker.nullness.qual
semua memiliki anotasi runtime ( @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 runtime lebih kecil daripada yang mungkin dipikirkan orang, tetapi mereka memiliki manfaat mengaktifkan alat untuk melakukan pemeriksaan runtime di samping waktu kompilasi.
Perbedaan penting lainnya adalah di mana dalam kode anotasi dapat digunakan. Ada dua pendekatan berbeda. Beberapa paket menggunakan konteks gaya JLS 9.6.4.1. Tabel berikut memberikan gambaran:
METODE PARAMETER BIDANG LOCAL_VARIABLE
android.support.annotation XXX
edu.umd.cs.findbugs.annotations XXXX
org.jetbrains.annotation XXXX
lombok XXXX
javax.validation.constraints XXX
org.eclipse.jdt.annotation, javax.annotationdan org.checkerframework.checker.nullness.qualgunakan konteks yang didefinisikan dalam JLS 4.11, yang menurut saya cara yang tepat untuk melakukannya.
Ini meninggalkan kita
javax.annotation
org.checkerframework.checker.nullness.qual
di babak ini.
Kode
Untuk membantu Anda membandingkan detail lebih lanjut sendiri, saya mencantumkan kode setiap anotasi di bawah ini. Untuk memudahkan perbandingan, saya menghapus komentar, impor, dan @Documentedanotasi. (mereka semua memiliki @Documentedkecuali kelas dari paket Android). Saya memesan ulang garis dan @Targetbidang 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 @Nullableimplementasinya:
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 punya @Nullable, jadi saya daftar secara terpisah; Lombok memiliki hal yang cukup membosankan @NonNull. Di javax.validation.constraintsdalam @NonNullsebenarnya @NotNull
dan memiliki implementasi gondrong.
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();
}
}
Dukung
Dari pengalaman saya, javax.annotationsetidaknya didukung oleh Eclipse dan Framework Checker di luar kotak.
Ringkasan
Anotasi ideal saya adalah java.annotationsintaks dengan implementasi Framework Checker.
Jika Anda tidak bermaksud menggunakan Kerangka Pemeriksa javax.annotation( JSR-305 ) masih merupakan taruhan terbaik Anda untuk saat ini.
Jika Anda bersedia untuk membeli Kerangka Pemeriksa gunakan saja org.checkerframework.checker.nullness.qual.
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
lombokdari lombokkomitf6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
javax.validation.constraints dari validation-api-1.0.0.GA-sources.jar