Почему пропущенная аннотация не вызывает 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, или это причуды JVM от Sun? В чем смысл?

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

82
задан Matt McHenry 25 August 2010 в 15:14
поделиться

3 ответа

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

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

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

85
ответ дан 24 November 2019 в 09:18
поделиться

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

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

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

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

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

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

32
ответ дан 24 November 2019 в 09:18
поделиться

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

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

7
ответ дан 24 November 2019 в 09:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: