Mengapa anotasi yang hilang tidak menyebabkan ClassNotFoundException saat runtime?

91

Perhatikan kode berikut:

A. java:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@interface A{}

C. java:

import java.util.*;

@A public class C {
        public static void main(String[] args){
                System.out.println(Arrays.toString(C.class.getAnnotations()));
        }
}

Mengompilasi dan menjalankan berfungsi seperti yang diharapkan:

$ javac *.java
$ java -cp . C
[@A()]

Tapi kemudian pertimbangkan ini:

$ rm A.class
$ java -cp . C
[]

Saya akan mengharapkannya untuk melempar ClassNotFoundException, karena @Ahilang. Namun, sebaliknya, anotasi akan dihapus secara diam-diam.

Apakah perilaku ini didokumentasikan di JLS di suatu tempat, atau ini merupakan kekhasan dari JVM Sun? Apa alasannya?

Tampaknya nyaman untuk hal-hal seperti javax.annotation.Nonnull(yang sepertinya memang seharusnya demikian @Retention(CLASS)), tetapi untuk banyak anotasi lainnya sepertinya hal itu dapat menyebabkan berbagai hal buruk terjadi pada waktu proses.

Matt McHenry
sumber

Jawaban:

90

Dalam draf publik sebelumnya untuk JSR-175 (anotasi), hal itu dibahas jika compiler dan runtime harus mengabaikan anotasi yang tidak diketahui, untuk memberikan penggabungan yang lebih longgar antara penggunaan dan deklarasi anotasi. Contoh spesifiknya adalah penggunaan penjelasan khusus server aplikasi pada EJB untuk mengontrol konfigurasi penerapan. Jika kacang yang sama harus digunakan pada server aplikasi yang berbeda, akan lebih mudah jika runtime mengabaikan anotasi yang tidak dikenal alih-alih memunculkan NoClassDefFoundError.

Meskipun kata-katanya agak kabur, saya berasumsi bahwa perilaku yang Anda lihat ditentukan di JLS 13.5.7: "... menghapus anotasi tidak berpengaruh pada hubungan yang benar dari representasi biner program dalam bahasa pemrograman Java . " Saya menafsirkan ini seolah-olah anotasi dihapus (tidak tersedia saat runtime), program harus tetap terhubung dan berjalan dan ini menyiratkan bahwa anotasi yang tidak dikenal diabaikan begitu saja ketika diakses melalui refleksi.

Rilis pertama Sun JDK 5 tidak mengimplementasikan ini dengan benar, tetapi telah diperbaiki di 1.5.0_06. Anda dapat menemukan bug 6322301 yang relevan dalam basis data bug, tetapi bug tersebut tidak menunjuk ke spesifikasi apa pun kecuali mengklaim bahwa "menurut petunjuk spesifikasi JSR-175, anotasi yang tidak dikenal harus diabaikan oleh getAnnotations".

jarnbjo
sumber
35

Mengutip JLS:

9.6.1.2 Penjelasan Retensi mungkin hanya ada dalam kode sumber, atau mungkin ada dalam bentuk biner kelas atau antarmuka. Anotasi yang ada dalam biner mungkin atau mungkin tidak tersedia pada waktu proses melalui pustaka reflektif platform Java.

Annotation type annotation. Retensi digunakan untuk memilih di antara kemungkinan di atas. Jika anotasi a sesuai dengan tipe T, dan T memiliki anotasi (meta-) m yang sesuai dengan anotasi.

  • Jika m memiliki elemen yang nilainya annotation.RetentionPolicy.SOURCE, maka compiler Java harus memastikan bahwa a tidak ada dalam representasi biner kelas atau antarmuka tempat a muncul.
  • Jika m memiliki elemen yang nilainya annotation.RetentionPolicy.CLASS, atau annotation.RetentionPolicy.RUNTIME, compiler Java harus memastikan bahwa a diwakili dalam representasi biner kelas atau antarmuka tempat a muncul, kecuali m menganotasi deklarasi variabel lokal . Anotasi pada deklarasi variabel lokal tidak pernah dipertahankan dalam representasi biner.

Jika T tidak memiliki anotasi (meta-) m yang sesuai dengan annotation.Retention, maka compiler Java harus memperlakukan T seolah-olah memiliki meta-annotation m dengan elemen yang nilainya annotation.RetentionPolicy.CLASS.

Jadi RetentionPolicy.RUNTIME memastikan bahwa anotasi dikompilasi ke dalam biner tetapi anotasi yang ada dalam biner tidak harus tersedia saat runtime

Guillaume
sumber
9

jika Anda benar-benar memiliki kode yang membaca @A dan melakukan sesuatu dengannya, kode tersebut memiliki ketergantungan pada kelas A, dan itu akan memunculkan ClassNotFoundException.

jika tidak, yaitu tidak ada kode yang secara khusus peduli tentang @A, maka @A dapat dikatakan tidak terlalu penting.

tak ternilai
sumber