Почему отсутствующая аннотация не вызывает исключение ClassNotFoundException во время выполнения?

Рассмотрим следующий код:

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()));
        }
}

Компиляция и работа работают как ожидалось:

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

Но тогда рассмотрим это:

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

Я бы ожидал, что он выбросит ClassNotFoundException, так как @A отсутствует. Но вместо этого он молча отключает аннотацию.

Является ли это поведение документированным в JLS где-то, или это причуда Sun JVM? Какое обоснование для этого?

Кажется, это удобно для таких вещей, как javax.annotation.Nonnull (похоже, в любом случае это должно было быть @Retention(CLASS)), но для многих других аннотаций кажется, что это может вызвать различные плохие вещи во время выполнения.

Ответ 1

В предыдущих публичных проектах для JSR-175 (аннотации) обсуждалось, должны ли компилятор и среда выполнения игнорировать неизвестные аннотации, чтобы обеспечить более слабую связь между использованием и объявлением аннотаций. Конкретным примером является использование аннотаций конкретных серверов приложений в EJB для управления конфигурацией развертывания. Если тот же самый bean должен быть развернут на другом сервере приложений, было бы удобно, если бы среда выполнения просто игнорировала неизвестные аннотации вместо повышения NoClassDefFoundError.

Даже если формулировка немного расплывчата, я предполагаю, что поведение, которое вы видите, указано в JLS 13.5.7: "... удаление аннотаций не влияет на правильную привязку двоичных представлений программ в Язык программирования Java". Я интерпретирую это так, как будто аннотации удаляются (недоступны во время выполнения), программа все равно должна связываться и запускаться, и это означает, что неизвестные аннотации просто игнорируются при доступе через отражение.

Первая версия Sun JDK 5 не реализовала это правильно, но была исправлена ​​в 1.5.0_06. Вы можете найти соответствующую ошибку 6322301 в базе данных ошибок, но она не указывает на какие-либо спецификации, за исключением того, что "согласно JSR- 175 spec lead, неизвестные аннотации должны быть проигнорированы getAnnotations".

Ответ 2

Цитирование JLS:

9.6.1.2 Сохранение Аннотации могут присутствовать только в исходном коде или они могут присутствовать в двоичной форме класса или интерфейса. Аннотации который присутствует в двоичной форме или могут быть недоступны во время выполнения через отражающие библиотеки Java платформы.

Тип аннотации annotation.Retention используется для выбора среди вышеупомянутых возможностей. Если аннотация a соответствует типу T, и T имеет (meta-) аннотацию m, которая соответствует аннотации. то:

  • Если m имеет элемент, значение которого является аннотацией .RetentionPolicy.SOURCE, то компилятор Java должен обеспечить, чтобы a нет в двоичном представление класса или интерфейс, в котором появляется символ.
  • Если m имеет элемент, значение которого представляет собой аннотацию. RetentionPolicy.CLASS или annotation.RetentionPolicy.RUNTIME a Компилятор Java должен убедиться, что a представленный в двоичном представление класса или интерфейс, в котором появляется a, если m аннотирует локальную переменную декларация. Аннотации на локальном объявление переменной никогда не сохраняется в двоичном представлении.

Если T не имеет (мета) аннотации m, что соответствует annotation.Retention, затем Java компилятор должен обрабатывать T, как если бы он имеют такую ​​мета-аннотацию m с элемент, значение которого annotation.RetentionPolicy.CLASS.

Таким образом, RetentionPolicy.RUNTIME обеспечивает компиляцию аннотации в двоичный файл, но аннотация, содержащаяся в двоичном формате, не должна быть доступна во время выполнения

Ответ 3

если у вас действительно есть код, который читает @A и что-то делает с ним, код имеет зависимость от класса A, и он будет генерировать ClassNotFoundException.

если нет, т.е. никакой код не заботится конкретно о @A, тогда он может утверждать, что @A не имеет большого значения.

Ответ 4

Аннотации не оказывают прямого влияния на работу кода, который они комментируют.
Однако, используя @Retention(RetentionPolicy.RUNTIME), аннотации становятся доступными во время выполнения.

Теперь, я предполагаю, что @Retention недоступен и поэтому игнорируется. Это означает, что другие аннотации не доступны во время выполнения.
Исключение не существует, поскольку по умолчанию аннотации игнорируются. Только при наличии @Retention они рассматриваются.

Возможно, если вы сделаете shure @Retention, будет жалоба. (не уверен в этом)