Annotazioni Predefinite in Java
- Le annotazioni predefinite in Java forniscono metadati utili per il compilatore e gli strumenti di sviluppo.
- Queste annotazioni possono essere utilizzate per specificare la politica di ritenzione, il target, l'eredità e altre caratteristiche delle annotazioni.
- Le annotazioni predefinite includono
@Retention
,@Documented
,@Target
,@Inherited
,@Override
,@Deprecated
,@FunctionalInterface
,@SafeVarargs
e@SuppressWarnings
. - Comprendere come utilizzare queste annotazioni è fondamentale per scrivere codice Java ben strutturato e mantenibile.
Le Annotazioni Predefinite
Java definisce molte annotazioni predefinite. La maggior parte sono specializzate, ma nove sono per uso generale.
Di queste, quattro sono importate da java.lang.annotation
:
@Retention
@Documented
@Target
@Inherited
.
Cinque, invece, sono incluse in java.lang
:
@Override
@Deprecated
@FunctionalInterface
@SafeVarargs
@SuppressWarnings
Studieremo queste annotazioni in dettaglio in questa lezione.
Altre Annotazioni Predefinite Speciali
Il package java.lang.annotation
include anche le annotazioni Repeatable
e Native
.
Repeatable supporta le annotazioni ripetibili, che vedremo nelle prossime lezioni.
Native annota un campo che può essere accessibile da codice nativo ossia codice Java che interagisce con codice scritto in C o C++.
Annotazione Retention
L'annotazione @Retention
è progettata per essere utilizzata solo come annotazione per un'altra annotazione.
Specifica la politica di ritenzione come descritto in precedenza.
Ecco un esempio di come utilizzare l'annotazione @Retention
:
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value();
}
Annotazione Documented
L'annotazione @Documented
è un'annotazione marcatrice che indica a uno strumento (un IDE o un editor) che un'annotazione deve essere documentata. È progettata per essere utilizzata solo come annotazione per una dichiarazione di annotazione.
import java.lang.annotation.*;
@Documented
public @interface MyAnnotation {
String value();
}
@MyAnnotation("Example")
public class Example {
public void myMethod() {
// ...
}
}
Annotazione Target
L'annotazione @Target
specifica i tipi di elementi ai quali un'annotazione può essere applicata.
È progettata per essere utilizzata solo come annotazione per un'altra annotazione. @Target
prende un argomento, che è un array di costanti dell'enumerazione ElementType
.
Questo argomento specifica i tipi di dichiarazioni alle quali l'annotazione può essere applicata. Le costanti sono mostrate qui insieme al tipo di dichiarazione al quale corrispondono:
Costante Target | L'annotazione può essere applicata a |
---|---|
ANNOTATION_TYPE |
Un'altra annotazione |
CONSTRUCTOR |
Costruttore |
FIELD |
Campo |
LOCAL_VARIABLE |
Variabile locale |
METHOD |
Metodo |
MODULE |
Modulo |
PACKAGE |
Pacchetto |
PARAMETER |
Parametro |
RECORD_COMPONENT |
Un componente di un record (Aggiunto da JDK 16) |
TYPE |
Classe, interfaccia o enumerazione |
TYPE_PARAMETER |
Parametro di tipo |
TYPE_USE |
Uso di tipo |
È possibile specificare uno o più di questi valori in un'annotazione @Target
.
Per specificare valori multipli, è necessario specificarli all'interno di una lista delimitata da parentesi graffe. Ad esempio, per specificare che un'annotazione si applica solo a campi e variabili locali, è possibile utilizzare questa annotazione @Target
:
@Target(
{
ElementType.FIELD,
ElementType.LOCAL_VARIABLE
}
)
Se non si utilizza @Target
, allora l'annotazione può essere utilizzata su qualsiasi dichiarazione. Per questa ragione, è spesso una buona idea specificare esplicitamente il target o i target in modo da indicare chiaramente gli usi previsti di un'annotazione.
Ad esempio, volendo creare un'annotazione personalizzata che si applica solo a metodi, si potrebbe definire l'annotazione in questo modo:
import java.lang.annotation.*;
@Target(ElementType.METHOD)
public @interface MyMethodAnnotation {
String value();
}
Annotazione Inherited
@Inherited
è un'annotazione marcatrice che può essere utilizzata solo su un'altra dichiarazione di annotazione.
Inoltre, influisce solo sulle annotazioni che verranno utilizzate su dichiarazioni di classe.
@Inherited
fa sì che l'annotazione di una superclasse sia ereditata da una sottoclasse. Pertanto, quando viene fatta una richiesta per un'annotazione specifica alla sottoclasse, se quell'annotazione non è presente nella sottoclasse, allora viene controllata la sua superclasse. Se quell'annotazione è presente nella superclasse, e se è annotata con @Inherited
, allora quell'annotazione verrà restituita.
In parole povere, se un'annotazione è marcata con @Inherited
, allora le sottoclassi erediteranno quell'annotazione dalla superclasse. Ciò normalmente non avviene in caso contrario.
Ecco un esempio di come utilizzare l'annotazione @Inherited
:
import java.lang.annotation.*;
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyInheritedAnnotation {
String value();
}
@MyInheritedAnnotation("Superclasse")
class SuperClass {
}
class SubClass extends SuperClass {
}
public class Main {
public static void main(String[] args) {
// Controlla se la sottoclasse ha l'annotazione
if (SubClass.class.isAnnotationPresent(MyInheritedAnnotation.class)) {
MyInheritedAnnotation annotation =
SubClass.class.getAnnotation(MyInheritedAnnotation.class);
System.out.println("Sottoclasse ha l'annotazione: " + annotation.value());
} else {
System.out.println("Sottoclasse non ha l'annotazione.");
}
}
}
Eseguendo questo programma, si otterrà l'output:
Sottoclasse ha l'annotazione: Superclasse
Questo dimostra che la sottoclasse ha ereditato l'annotazione dalla superclasse grazie all'uso di @Inherited
.
Annotazione Override
@Override
è un'annotazione marcatore che può essere utilizzata solo sui metodi.
Un metodo annotato con @Override
deve sovrascrivere un metodo da una superclasse. Se non lo fa, si verificherà un errore in fase di compilazione.
Viene utilizzata per assicurarsi che un metodo della superclasse sia effettivamente sovrascritto, e non semplicemente sovraccaricato.
Ecco un esempio di come utilizzare l'annotazione @Override
:
class SuperClass {
void display() {
System.out.println("Metodo della Superclasse");
}
}
class SubClass extends SuperClass {
@Override
void display() {
System.out.println("Metodo della Sottoclasse");
}
}
public class Main {
public static void main(String[] args) {
SubClass sub = new SubClass();
sub.display(); // Visualizza "Metodo della Sottoclasse"
}
}
In questo esempio, il metodo display()
nella classe SubClass
è annotato con @Override
per indicare che sta sovrascrivendo il metodo display()
della superclasse SuperClass
.
Facendo così il compilatore controlla due cose:
- Che il metodo
display()
nella classeSubClass
stia effettivamente sovrascrivendo un metodo della superclasse. - Che la firma del metodo (nome, parametri e tipo di ritorno) corrisponda esattamente a quella del metodo della superclasse.
Annotazione Deprecated
@Deprecated
indica che una dichiarazione è obsoleta e non è raccomandato il suo utilizzo.
A partire da JDK 9, @Deprecated
consente anche di specificare la versione Java in cui è avvenuta la deprecazione e se l'elemento deprecato è destinato alla rimozione.
Ecco un esempio di come utilizzare l'annotazione @Deprecated
:
class OldClass {
@Deprecated
void oldMethod() {
System.out.println("Questo metodo è obsoleto.");
System.out.println("Utilizzare il nuovo metodo.");
}
void newMethod() {
System.out.println("Questo è il nuovo metodo.");
}
}
public class Main {
public static void main(String[] args) {
OldClass old = new OldClass();
old.oldMethod(); // Visualizza "Questo metodo è obsoleto."
old.newMethod(); // Visualizza "Questo è il nuovo metodo."
}
}
In questo esempio, il metodo oldMethod()
è annotato con @Deprecated
, indicando che non dovrebbe più essere utilizzato. Quando si chiama questo metodo, il compilatore può generare un avviso per informare lo sviluppatore che il metodo è obsoleto.
Volendo specificare la versione in cui il metodo è stato deprecato e se è destinato alla rimozione, si può utilizzare la seguente sintassi:
@Deprecated(since = "1.0", forRemoval = true)
void oldMethod() {
System.out.println("Questo metodo è obsoleto.");
System.out.println("Sarà rimosso in una futura versione.");
}
Annotazione FunctionalInterface
@FunctionalInterface
è un'annotazione marker progettata per l'uso su interfacce.
Indica che l'interfaccia annotata è un'interfaccia funzionale. Un'interfaccia funzionale è un'interfaccia che contiene uno e un solo metodo astratto. Le interfacce funzionali sono utilizzate dalle espressioni lambda. Studieremo queste interfacce in dettaglio nelle prossime lezioni.
Se l'interfaccia annotata non è un'interfaccia funzionale, verrà segnalato un errore di compilazione. È importante comprendere che @FunctionalInterface
non è necessaria per creare un'interfaccia funzionale. Qualsiasi interfaccia con esattamente un metodo astratto è, per definizione, un'interfaccia funzionale. Pertanto, @FunctionalInterface
è puramente informativa. Funziona allo stesso modo di **@Override**
, in quanto fornisce un'indicazione al compilatore e agli sviluppatori che l'interfaccia è destinata a essere utilizzata come interfaccia funzionale.
Ecco un esempio di come utilizzare l'annotazione @FunctionalInterface
:
@FunctionalInterface
interface MyFunctionalInterface {
void execute();
}
Annotazione SafeVarargs
@SafeVarargs
è un'annotazione marker che può essere applicata a metodi e costruttori.
Indica che non si verificano azioni non sicure relative a un parametro varargs.
Viene utilizzata per sopprimere avvisi non controllati su codice altrimenti sicuro in quanto si riferisce a tipi vararg non reificabili e istanziazione di array parametrizzati.
Un tipo non reificabile è, essenzialmente, un tipo generico. Studieremo i tipi generici, o Generics, nelle prossime lezioni.
Deve essere applicata solo a metodi varargs o costruttori. I metodi devono anche essere static
, final
, o private
.
Ecco un esempio di come utilizzare l'annotazione @SafeVarargs
:
import java.util.Arrays;
public class Main {
@SafeVarargs
public static sommaArray(int... numbers) {
int sum = Arrays.stream(numbers).sum();
System.out.println("Somma: " + sum);
}
public static void main(String[] args) {
sommaArray(1, 2, 3); // Visualizza "Somma: 6"
sommaArray(4, 5, 6); // Visualizza "Somma: 15"
}
}
Annotazione SuppressWarnings
@SuppressWarnings
specifica che uno o più avvertimenti che potrebbero essere emessi dal compilatore devono essere soppressi.
Gli avvertimenti da sopprimere sono specificati per nome, in forma di stringa.