Sejak JSR 305 (yang tujuannya adalah untuk membakukan @NonNull
dan @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.constraints
atau 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.annotation
karena 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 javax
ke java
masa 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 @NonNull
anotasi 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 menyebutnya @NotNull
dan memiliki implementasi
Semua @Nullable
anotasi memiliki garis
public @interface Nullable {}
kecuali untuk (lagi) org.jetbrains.annotations
dengan 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.annotation
dan org.checkerframework.checker.nullness.qual
gunakan 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 @Documented
anotasi. (mereka semua memiliki @Documented
kecuali kelas dari paket Android). Saya memesan ulang garis dan @Target
bidang 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 @Nullable
implementasinya:
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.constraints
dalam @NonNull
sebenarnya @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.annotation
setidaknya didukung oleh Eclipse dan Framework Checker di luar kotak.
Ringkasan
Anotasi ideal saya adalah java.annotation
sintaks 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
lombok
dari lombok
komitf6da35e4c4f3305ecd1b415e2ab1b9ef8a9120b4
javax.validation.constraints
dari validation-api-1.0.0.GA-sources.jar