Waarom veroorzaakt een ontbrekende annotatie geen ClassNotFoundException tijdens runtime?

Denk aan de volgende code:

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

Het compileren en uitvoeren werkt zoals verwacht:

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

Maar bedenk dan dit:

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

Ik had verwacht dat het een ClassNotFoundExceptionzou geven, aangezien @Aontbreekt. Maar in plaats daarvan laat het de annotatie in stilte vallen.

Is dit gedrag ergens in de JLS gedocumenteerd, of is het een eigenaardigheid van Sun’s JVM? Wat is de reden hiervoor?

Het lijkt handig voor zaken als javax.annotation.Nonnull(wat toch lijkt alsof het @Retention(CLASS)had moeten zijn), maar voor veel andere annotaties het lijkt erop dat het tijdens runtime verschillende slechte dingen kan veroorzaken.


Antwoord 1, autoriteit 100%

In de eerdere openbare concepten voor JSR-175 (annotaties) werd besproken of de compiler en runtime onbekende annotaties moesten negeren, om een ​​lossere koppeling te bieden tussen het gebruik en de declaratie van annotaties. Een specifiek voorbeeld was het gebruik van applicatieserverspecifieke annotaties op een EJB om de implementatieconfiguratie te beheren. Als dezelfde bean op een andere applicatieserver zou worden geïmplementeerd, zou het handig zijn geweest als de runtime de onbekende annotaties gewoon negeerde in plaats van een NoClassDefFoundError te genereren.

Zelfs als de bewoording een beetje vaag is, neem ik aan dat het gedrag dat je ziet gespecificeerd is in JLS 13.5.7: “… het verwijderen van annotaties heeft geen effect op de correcte koppeling van de binaire representaties van programma’s in de Java-programmering taal.” Ik interpreteer dit alsof annotaties worden verwijderd (niet beschikbaar tijdens runtime), het programma nog steeds moet linken en draaien en dat dit impliceert dat de onbekende annotaties eenvoudigweg worden genegeerd wanneer ze via reflectie worden geopend.

De eerste release van Sun’s JDK 5 implementeerde dit niet correct, maar het werd opgelost in 1.5.0_06. Je kunt de relevante bug 6322301vinden in de bugdatabase, maar dat is niet het geval verwijzen naar alle specificaties, behalve dat “volgens de JSR-175 spec lead, onbekende annotaties moeten worden genegeerd door getAnnotations”.


Antwoord 2, autoriteit 38%

De JLS citeren:

9.6.1.2 RetentieAnnotaties mogen alleen in de broncode voorkomen, of
ze kunnen aanwezig zijn in de binaire vorm
van een klasse of interface. Een annotatie
die aanwezig is in de binaire kan of
mogelijk niet beschikbaar tijdens runtime via
de reflecterende bibliotheken van de Java
platform.

Het annotatietype
annotatie.Retentie wordt gebruikt om te kiezen
onder de bovenstaande mogelijkheden. Als een
annotatie a komt overeen met een type T,
en T heeft een (meta-)annotatie m dat
komt overeen met annotatie.Retentie,
dan:

  • Als m een ​​element heeft waarvan de waarde annotation.RetentionPolicy.SOURCE is,
    dan moet een Java-compiler ervoor zorgen dat:
    a is niet aanwezig in het binaire bestand
    representatie van de klas of
    interface waarin een verschijnt.
  • Als m een ​​element heeft waarvan de waarde annotation.RetentionPolicy.CLASS is, of
    annotation.RetentionPolicy.RUNTIME a
    Java-compiler moet ervoor zorgen dat een is
    vertegenwoordigd in de binaire
    representatie van de klas of
    interface waarin a verschijnt, tenzij m
    annoteert een lokale variabele
    verklaring. Een annotatie op een lokale
    variabele declaratie wordt nooit bewaard
    in de binaire weergave.

Als T geen (meta-)annotatie heeft
m dat overeenkomt met
annotatie.Retentie, dan een Java
compiler moet T behandelen alsof het dat doet
hebben zo’n meta-annotatie m met an
element waarvan de waarde is
annotation.RetentionPolicy.CLASS.

Dus RetentionPolicy.RUNTIME zorgt ervoor dat de annotatie wordt gecompileerd in het binaire bestand, maar een annotatie die aanwezig is in het binaire bestand hoeft niet beschikbaar te zijn tijdens runtime


Antwoord 3, autoriteit 10%

Als je daadwerkelijk code hebt die @A leest en er iets mee doet, is de code afhankelijk van klasse A en zal ClassNotFoundException worden gegenereerd.

zo niet, d.w.z. geen enkele code geeft specifiek om @A, dan is het aannemelijk dat @A er niet echt toe doet.

Other episodes