Apa arti java.util. @ Nullable?

56

Saya membaca kode Guava di mana saya menemukan anotasi java.util.@Nullabledalam beberapa kode. Saya tahu artinya @Nullable, tetapi saya tidak mengerti yang ini. Secara khusus, saya tidak dapat menemukan kelas yang dipanggil Nullabledalam paket java.util. Tolong, seseorang memberi tahu saya apa artinya ini java.util.@Nullable:

public static <T> java.util.@Nullable Optional<T> toJavaUtil(
    @Nullable Optional<T> googleOptional) {
  return googleOptional == null ? null : googleOptional.toJavaUtil();
}
SetDaemon
sumber
4
@ PaulRooney, saya rasa tidak. Yang Nullablemereka bicarakan sepertinya ada javax.annotation, bukan java.util.
Joseph Sible-Reinstate Monica
3
Saya ingin tahu apakah ini mungkin Java-sintaks-isme yang aneh. Secara khusus, dapatkah ini pada dasarnya berarti public static <T> @Nullable java.util.Optional<T> toJavaUtil, tetapi Java tidak membiarkan Anda menulis seperti itu?
Joseph Sible-Reinstate Monica
1
@JosephSible public static <T> @Nullable java.util.Optional<T> toJavaUtilmemang tidak mungkin, tetapi tidak ada alasan untuk menggunakan FQN Optionalketika impor sudah ada.
Tom
6
@ Tom Ada lagi Optionalruang lingkup di sana.
Joseph Sible-Reinstate Monica
2
Terima kasih teman-teman, saya menemukan ini di Kerangka Pemeriksa. @Nullable java.util.List The correct Java syntax to write an annotation on a fully-qualified type name is to put the annotation on the simple name part, as in java.util.@Nullable List. But, it’s usually better to add import java.util.List to your source file, so that you can just write @Nullable List.
SetDaemon

Jawaban:

37

Baris public static <T> java.util.@Nullable Optional<T> toJavaUtilditulis seperti ini, karena gaya yang biasa public static <T> @Nullable java.util.Optional<T> toJavaUtiltidak valid. Ini didefinisikan dalam JLS §9.7.4 :

Ini adalah kesalahan waktu kompilasi jika anotasi tipe T berlaku untuk tipe (atau bagian mana pun dari tipe) dalam konteks tipe, dan T berlaku dalam konteks tipe, dan anotasi tidak dapat diterima.

Sebagai contoh, asumsikan TA jenis penjelasan yang meta-dijelaskan dengan adil @Target(ElementType.TYPE_USE). Persyaratan @TA java.lang.Objectdan java.@TA lang.Objectilegal karena nama sederhana yang paling dekat dengan @TA diklasifikasikan sebagai nama paket. Di sisi lain, java.lang.@TA Objectitu legal.

Deklarasi tipe org.checkerframework.checker.nullness.qual@Nullableadalah:

@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})

Jadi itu berlaku untuk aturan ini.

Bahwa struktur ini tidak menghentikan eksekusi, karena paket java.utildan nama kelas Optionalterpecah, dapat dilihat ketika kita melihat kode yang dikompilasi menggunakan javap -c [compiled class name]:

class just.a.test.Main {
  just.a.test.Main();
    Code:
       0: aload_0
       1: invokespecial #1          // Method java/lang/Object."<init>":()V
       4: return

  public static <T> java.util.Optional<T> toJavaUtil(blub.Optional<T>);
    Code:
       0: aload_0
       1: ifnonnull     8
       4: aconst_null
       5: goto          12
       8: aload_0
       9: invokevirtual #2          // Method blub/Optional.toJavaUtil:()Ljava/util/Optional;
      12: areturn
}

( blub.Optionaladalah kelas lokal tempat saya menyalin kode Guava, untuk mendapatkan contoh minimal untuk de- / kompilasi)

Seperti yang Anda lihat, anotasi tidak ada lagi di sana. Ini hanya penanda untuk kompiler untuk mencegah peringatan ketika metode mengembalikan nol (dan petunjuk untuk pembaca kode sumber), tetapi itu tidak akan dimasukkan dalam kode yang dikompilasi.


Kesalahan kompiler ini juga berlaku untuk variabel seperti:

private @Nullable2 java.util.Optional<?> o;

Tetapi dapat diterima ketika anotasi tambahan mendapatkan tipe target ElementType.FIELD, seperti yang tertulis dalam klausa JLS yang sama:

Jika TA ditambahkan dengan meta-anotasi @Target(ElementType.FIELD), maka istilah @TA java.lang.Objecttersebut legal di lokasi yang merupakan deklarasi dan tipe konteks, seperti deklarasi lapangan @TA java.lang.Object f;. Di sini, @TA dianggap berlaku untuk deklarasi f (dan bukan untuk tipe java.lang.Object) karena TA berlaku dalam konteks deklarasi lapangan.

Tom
sumber
11

Saat menggunakan anotasi, ini adalah sintaks yang digunakan ketika Anda ingin menulis nama yang sepenuhnya memenuhi syarat untuk jenis, alih-alih menambahkan pernyataan impor.

Mengutip dari manual framework checker :

Sintaks Java yang benar untuk menulis anotasi pada nama tipe yang sepenuhnya memenuhi syarat adalah dengan menempatkan anotasi pada bagian nama yang sederhana, seperti pada java.util. @ Nullable List. Tapi, biasanya lebih baik menambahkan import java.util.List ke file sumber Anda, sehingga Anda bisa menulis @Nullable List.

Ini juga disebutkan pada halaman 2 dari spesifikasi JSR308 yang dapat diunduh di sini . Ia mengatakan:

Anotasi tipe muncul di depan nama sederhana tipe tersebut, seperti pada @NonNull String atau java.lang. @ NonNull String.

Kartik
sumber
6

Agak aneh di sini adalah sintaks yang tidak biasa untuk menerapkan ElementType.TYPE_USEanotasi -ditargetkan. Jika Anda memeriksa dokumen Nullable , Anda akan melihat target yang tidak dikenal:

...
@Target(value={TYPE_USE,TYPE_PARAMETER})
<public @interface Nullable
...

Anotasi ini digunakan tepat sebelum nama sederhana dari tipe yang dianotasi, seperti pada kedua berikut ini:

public static <T> @Nullable Optional<T> toJavaUtil
public static <T> java.util.@Nullable Optional<T> toJavaUtil

Saya tidak tahu apa gunanya target semacam ini, jadi setelah membaca cepat, saya sampai ke contoh sederhana ini, yang mengambil metadata tipe kembali menggunakan anotasi yang memiliki target itu:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@interface InterceptedReturnValue {
    boolean value() default true;
}

Dan mengolahnya menggunakan:

public java.lang.@InterceptedReturnValue(true) String testMethod(String param) {
    return null;
}

public @InterceptedReturnValue(false) String testMethod(int param) {
    return null;
}

public static void main(String[] args) throws Exception {
    Method m = Main.class.getDeclaredMethod("testMethod", String.class);
    if(m.getAnnotatedReturnType().isAnnotationPresent(InterceptedReturnValue.class)) {
        InterceptedReturnValue config = m.getAnnotatedReturnType()
                .getAnnotation(InterceptedReturnValue.class);

        if(config.value()) {
            //logging return value enabled
        }
    }
}

Saya yakin banyak kerangka kerja yang bermanfaat, seperti checkerframework, membuat penggunaan yang paling tepat ElementType.TYPE_USE

ernest_k
sumber