Perché non è possibile estendere le annotazioni in Java?


227

Non capisco perché non vi sia ereditarietà nelle annotazioni Java, proprio come le classi Java. Penso che sarebbe molto utile.

Ad esempio: voglio sapere se una determinata annotazione è un validatore. Con l'ereditarietà, ho potuto navigare riflessivamente tra le superclassi per sapere se questa annotazione si estende a ValidatorAnnotation. Altrimenti, come posso raggiungere questo obiettivo?

Quindi, qualcuno può darmi una ragione per questa decisione di progettazione?


2
Si noti, a proposito, che tutte le annotazioni si estendono java.lang.annotation.Annotation, vale a dire qualsiasi annotazione è instanceofanche se questo fatto non è esplicitamente dichiarato.
Tomáš Záluský,

Risposte:


166

Sul motivo per cui non è stato progettato in questo modo puoi trovare la risposta nelle Domande frequenti sul design di JSR 175 , dove dice:

Perché non supporti il ​​sottotipo di annotazione (in cui un tipo di annotazione ne estende un altro)?

Ciò complica il sistema dei tipi di annotazione e rende molto più difficile scrivere "Strumenti specifici".

...

"Strumenti specifici": programmi che eseguono query sui tipi di annotazione noti di programmi esterni arbitrari. I generatori di stub, ad esempio, rientrano in questa categoria. Questi programmi leggeranno le classi annotate senza caricarle nella macchina virtuale, ma caricheranno le interfacce di annotazione.

Quindi, sì, immagino, il motivo è solo BACIO. Ad ogni modo, sembra che questo problema (insieme a molti altri) venga esaminato come parte di JSR 308 , e puoi persino trovare un compilatore alternativo con questa funzionalità già sviluppata da Mathias Ricken .


67
Beh, forse sono stupido, ma penso che sia un peccato non poter estendere le annotazioni solo per "mantenerlo semplice". Almeno, i progettisti Java non hanno pensato allo stesso modo per l'eredità di classe: P
sinuhepop,

2
Java 8 M7, non sembra supportare le annotazioni della sottoclasse. Che peccato.
Ceki,

2
@assylias JEP 104 non intende rendere possibile la sottoclasse delle annotazioni. JEP 104 è stato implementato in Java 8, ma non è ancora possibile sottoclassare le annotazioni (fare in modo che un'annotazione estenda un'altra annotazione).
Jesper,

71

Le annotazioni estendibili aggiungerebbero effettivamente l'onere di specificare e mantenere un altro sistema di tipi. E questo sarebbe un sistema di tipo abbastanza unico, quindi non potresti semplicemente applicare un paradigma di tipo OO.

Rifletti su tutti i problemi quando introduci il polimorfismo e l'ereditarietà in un'annotazione (ad es. Cosa succede quando l'annotazione secondaria modifica le specifiche dell'annotazione come la conservazione?)

E tutta questa complessità aggiunta per quale caso d'uso?

Vuoi sapere se una determinata annotazione appartiene a una categoria?

Prova questo:

@Target(ElementType.ANNOTATION_TYPE)
public @interface Category {
    String category();
}

@Category(category="validator")
public @interface MyFooBarValidator {

}

Come puoi vedere, puoi facilmente raggruppare e classificare le annotazioni senza indebito dolore utilizzando le strutture fornite.

Quindi, KISS è la ragione per non introdurre un sistema di tipo meta-tipo nel linguaggio Java.

[modifica ps]

Ho usato la stringa semplicemente per dimostrazione e in vista di una meta annotazione aperta. Per il tuo progetto, puoi ovviamente usare un enum di tipi di categoria e specificare più categorie ("eredità multipla") per una data annotazione. Si noti che i valori sono completamente fasulli e solo a scopo dimostrativo:

@Target(ElementType.ANNOTATION_TYPE)
public @interface Category {
    AnnotationCategory[] category();
}
public enum AnnotationCategory {
    GENERAL,
    SEMANTICS,
    VALIDATION,
    ETC
}

@Category(category={AnnotationCategory.GENERAL, AnnotationCategory.SEMANTICS})
public @interface FooBarAnnotation {

}


Non funziona verrà stampato falso:@Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) @interface C {}; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @C public @interface F {} class a{ @F public void S() {} } @Test public void blahTest() throws NoSuchMethodException { Method m = a.class.getMethod("S"); System.out.println(m.isAnnotationPresent(C.class)); }
user1615664

Stiamo annotando un'annotazione. a # S ha un'annotazione F. F stessa è annotata da C. Provalo.
alphazero,

Si, è corretto. Ma c'è un modo per farlo in modo più pulito piuttosto che leggere il proxy di annotazione?
user1615664

12

In un certo senso ce l'hai già con Annotazioni - meta Annotazioni. Se annoti un'annotazione con meta informazioni, ciò equivale in molti modi a estendere un'interfaccia aggiuntiva. Le annotazioni sono interfacce, quindi il polimorfismo non è realmente in gioco, e poiché sono di natura statica, non può esserci dispatching dinamico di runtime.

Nel tuo esempio di validatore, potresti semplicemente sull'annotazione ottenere il tipo annotato e vedere se ha una meta-annotazione di validatore.

L'unico caso d'uso in cui ho visto che l'ereditarietà sarebbe utile se volessi essere in grado di ottenere l'annotazione per super tipo, ma ciò aggiungerebbe un sacco di complessità, perché un determinato metodo o tipo potrebbe avere due di tali annotazioni su di esso, ciò significa che dovrebbe essere restituito un array anziché un singolo oggetto.

Quindi penso che la risposta definitiva sia che i casi d'uso sono esoterici e complicano i casi d'uso più standard rendendone la pena.


5

I progettisti del supporto per le annotazioni Java hanno apportato una serie di "semplificazioni" a scapito della comunità Java.

  1. Nessun sottotipo di annotazioni rende inutilmente brutte molte annotazioni complesse. Non si può semplicemente avere un attributo all'interno di un'annotazione che può contenere una di tre cose. È necessario disporre di tre attributi separati, che confondono gli sviluppatori e richiedono la convalida del runtime per garantire che venga utilizzato solo uno dei tre.

  2. Solo un'annotazione di un determinato tipo per sito. Ciò ha portato al modello di annotazione della raccolta completamente inutile. @Validation e @Validations, @Image e @Images, ecc.

Il secondo è stato risolto in Java 8, ma è troppo tardi. Molti framework sono stati scritti in base a ciò che era possibile in Java 5 e ora queste verruche API sono qui per rimanere per un bel po 'di tempo.


1
Non solo, rende anche impossibile definire proprietà con attributi annidati in modo ricorsivo, che è supportato dallo schema XML. Questo rende le annotazioni strettamente meno potenti di XML
user2833557

3

Potrei essere in ritardo di tre anni per rispondere a questa domanda, ma l'ho trovato interessante perché mi sono trovato nello stesso punto. Ecco la mia opinione su di esso. Puoi visualizzare le annotazioni come Enum. Forniscono un tipo di informazione a senso unico: usale o perdila.

Ho avuto una situazione in cui volevo simulare GET, POST, PUT e DELETE in un'app Web. Volevo così tanto avere un'annotazione "super" che si chiamava "HTTP_METHOD". In seguito mi sono reso conto che non aveva importanza. Bene, ho dovuto accontentarmi di usare un campo nascosto nel modulo HTML per identificare DELETE e PUT (perché POST e GET erano comunque disponibili).

Sul lato server, ho cercato un parametro di richiesta nascosto con il nome "_method". Se il valore era PUT o DELETE, ha la precedenza sul metodo di richiesta HTTP associato. Detto questo, non importava se avessi bisogno di estendere un'annotazione per fare il lavoro. Tutte le annotazioni sembravano uguali, ma sono state trattate diversamente sul lato server.

Quindi, nel tuo caso, elimina il prurito per estendere le annotazioni. Trattali come "marcatori". "Rappresentano" alcune informazioni e non necessariamente "manipolano" alcune informazioni.


2

Una cosa che mi viene in mente è la possibilità di avere più annotazioni. Quindi potresti aggiungere validatore e un'annotazione più specifica nello stesso posto. Ma potrei sbagliarmi :)


2

Non ci ho mai pensato ma ... sembra che tu abbia ragione, non ci sono problemi con la funzione di ereditarietà delle annotazioni (almeno non ci vedo il problema).

A proposito del tuo esempio con l' annotazione "validatore" , puoi sfruttare l' approccio "meta-annotazione" . Vale a dire applicare meta-annotazioni particolari all'intera interfaccia delle annotazioni.


Potrei essere in ritardo di tre anni per rispondere a questa domanda, ma l'ho trovato interessante perché mi sono trovato nello stesso punto.
mainas,

1

lo stesso problema che ho. No, non puoi. Mi sono "disciplinato" a scrivere proprietà nelle annotazioni per rispettare alcuni standard, quindi al di fuori quando si ottiene un'annotazione è possibile "annusare" che tipo di annotazione è per proprietà che ha.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.