Nella Guida a Java 6, alla lezione dedicata alla creazione delle Java Annotations, abbiamo trattato l'argomento solo per via teorica, lasciando la parte pratica a questo articolo che risulterà certamente più utile.
Diciamo subito che per la comprensione di questo articolo è fondamentale leggere l'intera parte dedicata alle Java Annotations sulla guida (quindi lezione di introduzione, Tipi di Annotations e creazione e utilizzo).
Ora vediamo quindi, con un esempio concreto, come creare un'annotazione personalizzata. Creeremo una semplice annotazione con alcuni campi e vedremo anche come leggerla a runtime.
Listato 1. Crea e legge un'annotazione
package it.html.annotations;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Una semplice annotation, usata per segnare gli elementi
* (classe, attributo, metodo), l'autore di quell'elemento e
* l'azienda.
*/
@Retention(RUNTIME)
public @interface MyAnnotation {
public String value();
public String author();
public String company() default "HTML.it";
}
L'annotazione viene creata con il comando @interface
, seguito dal nome dell'annotazione stessa. Badate che si tratta di classi java, pertanto esse seguono le stesse regole relative a package e import.
L'annotazione è (nel nostro caso) preceduta da un'altra annotazione, @Retention
, che specifica il livello di introspezione desiderato. Ne esistono tre: RUNTIME, CLASS e SOURCE.
Bisogna usare RUNTIME se si vuole che l'annotazione sia utilizzabile dalla JVM, durante l'esecuzione (a runtime, appunto). Bisogna usare CLASS se si vuole che l'annotazione sia visibile solo dal compilatore. Bisogna usare SOURCE se l'annotazione non deve essere visibile (quindi usata meramente come commento al codice sorgente).
Il corpo dell'annotation può contenere, o meno, delle proprietà. Nel secondo caso si parla di marker annotation, utilizzate per marcare uno specifico elemento. Il primo caso è quello che ci riguarda e, come vediamo dall'esempio, è molto simile a quanto si fa con le interfacce.
Noi abbiamo definito tre parametri, tutti e tre stringa nominati: value, author e company.
I tipi che possono essere utilizzati sono i tipi primitivi (i numeri), le stringhe, enum, class, altre annotazioni ed array dei tipi appena menzionati. Una possibilità è quella di definire dei valori di default, come abbiamo fatto nel caso del parametro company.
Ultima cosa da dire, prima di utilizzare l'annotazione appena creata, è che c'è la possibilità di creare le annotazioni per specifiche parti della classe. L'annotazione può essere creata avendo come target solo i metodi, oppure solo i costruttori. Potrebbe avere come target solo la classe o i suoi attributi. In tal caso si usa un'altra annotation, @Target, che specifica appunto il tipo per cui l'annotation è stata pensata. Se avessimo voluto utilizzarla avremmo dovuto scrivere:
@Retention(value=RUNTIME)
@Target(value=XYZ)
public @interface MyAnnotation{
...
Dove il valore XYZ doveva essere rimpiazzato con: TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE. Tutto dipende dal fatto di voler forzare un'annotazione ad uno specifico tipo.
Andiamo a vedere una classe che viene "annotata" attraverso l'annotazione appena creata.
Listato 2. Una classe che usa l'annotazione
package it.html.annotations;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//Qui la usiamo a livello di classe
@MyAnnotation(value="class",author="Pippo")
public class SimpleAnnotationUse {
//qui, a livello di attributo
@MyAnnotation(value="attribute",author="Pippo")
public String saluto;
//qui, a livello di costruttore
@MyAnnotation(value="costruttore",author="Pippo")
public SimpleAnnotationUse(){
this.saluto="Hello World!";
}
//qui, a livello di metodo
@MyAnnotation(value="method",author="Zio Paperone",company="Gugol")
public String getSaluto(){
return saluto;
}
...
}
Possiamo vedere che ogni elemento della classe (e la classe stessa) vengono annotati con il valore indicante l'elemento stesso, il nome dell'autore e l'azienda (laddove l'azienda manca, verrà recuperato il valore di default).
Listato 3. Classe che fa introspezione della classe, dei metodi e degli attributi
public static void main(String args[]) throws NoSuchMethodException, NoSuchFieldException{
System.out.println("Working Annotations...");
//Accesso alla classe
Class<SimpleAnnotationUse> c = SimpleAnnotationUse.class;
//Mostra annotazioni
System.out.println("Annotazione di classe: ");
System.out.println(c.getAnnotation( MyAnnotation.class ));
//Metodo costruttore
Constructor<SimpleAnnotationUse> constructor = c.getConstructor((Class[]) null);
System.out.println("Annotazione costruttore: ");
System.out.println(constructor.getAnnotation(MyAnnotation.class));
//Metodo getSaluto
Method method = c.getMethod( "getSaluto" );
System.out.println("Annotazione metodo getSaluto(): ");
System.out.println(method.getAnnotation(MyAnnotation.class));
//Campo saluto
Field field = c.getField("saluto");
System.out.println("Annotazione attributo saluto: ");
System.out.println(field.getAnnotation(MyAnnotation.class));
}
Il main proposto fa introspezione della classe, dei metodi e degli attributi, utilizzando il metodo getAnnotation()
. In questo caso stamperemo semplicemente il valore, ma i casi d'uso che si aprono sono veramente innumerevoli.