1. Ottenere informazioni sui campi
Qual è la differenza tra getFields() e getDeclaredFields()
In Java ogni classe ha dei campi (fields) — variabili dichiarate all’interno della classe. Con la riflessione possiamo conoscerne i nomi, i tipi, i modificatori di accesso (public/private) e accedervi a runtime. Il punto d’ingresso principale è l’oggetto Class<?>.
Metodi principali della classe Class per lavorare con i campi:
| Metodo | Cosa restituisce |
|---|---|
|
Un array di tutti i campi public della classe, dei suoi genitori e delle interfacce |
|
Un array di tutti i campi dichiarati in questa classe |
Analogia: getFields() — come una visita guidata solo nelle sale espositive (public), mentre getDeclaredFields() — anche nei locali tecnici (private, protected, package-private).
Esempio: stampiamo tutti i campi della classe
Supponiamo di avere una classe:
public class Person {
public String name;
private int age;
protected String email;
}
Otteniamo informazioni sui campi:
import java.lang.reflect.Field;
Class<?> clazz = Person.class;
// Tutti i campi public (inclusi quelli ereditati)
System.out.println("Public fields:");
for (Field field : clazz.getFields()) {
System.out.println(field.getName() + " : " + field.getType().getSimpleName());
}
// Tutti i campi dichiarati (inclusi i private, solo quelli propri)
System.out.println("\nDeclared fields:");
for (Field field : clazz.getDeclaredFields()) {
System.out.println(field.getName() + " : " + field.getType().getSimpleName());
}
Risultato:
Public fields:
name : String
Declared fields:
name : String
age : int
email : String
Come ottenere i modificatori di un campo?
Ogni campo (Field) ha dei modificatori (public/private/protected, static, final, ecc.). Si possono ottenere tramite getModifiers() e convertire in stringa con Modifier:
import java.lang.reflect.Modifier;
for (Field field : clazz.getDeclaredFields()) {
int mods = field.getModifiers();
System.out.println(field.getName() + " : " + Modifier.toString(mods));
}
2. Ottenere informazioni sui metodi
I metodi getMethods() e getDeclaredMethods()
I metodi sono le azioni che un oggetto può eseguire. Con la riflessione è possibile conoscere quali metodi possiede una classe, i loro parametri, i tipi restituiti, i modificatori e le annotazioni.
| Metodo | Cosa restituisce |
|---|---|
|
Tutti i metodi public della classe e dei suoi genitori (incluso Object) |
|
Tutti i metodi dichiarati in questa classe (inclusi i private) |
Esempio: stampiamo tutti i metodi della classe
import java.lang.reflect.Method;
System.out.println("Public methods:");
for (Method method : clazz.getMethods()) {
System.out.println(method.getName());
}
System.out.println("\nDeclared methods:");
for (Method method : clazz.getDeclaredMethods()) {
System.out.println(method.getName());
}
Risultato (per Person):
Public methods:
getClass
hashCode
equals
toString
notify
notifyAll
wait
wait
wait
Declared methods:
(niente, se in Person non sono dichiarati metodi propri)
Aggiungiamo un metodo a Person:
public class Person {
public String name;
private int age;
protected String email;
public void sayHello() {
System.out.println("Hi!");
}
}
Ora getDeclaredMethods() lo mostrerà.
Come ottenere i parametri e il tipo restituito di un metodo?
for (Method method : clazz.getDeclaredMethods()) {
System.out.print(Modifier.toString(method.getModifiers()) + " ");
System.out.print(method.getReturnType().getSimpleName() + " ");
System.out.print(method.getName() + "(");
Class<?>[] params = method.getParameterTypes();
for (int i = 0; i < params.length; i++) {
System.out.print(params[i].getSimpleName());
if (i < params.length - 1) System.out.print(", ");
}
System.out.println(");");
}
Output:
public void sayHello();
3. Ottenere informazioni sui costruttori
I costruttori sono metodi speciali utilizzati per creare oggetti.
| Metodo | Cosa restituisce |
|---|---|
|
Tutti i costruttori public |
|
Tutti i costruttori dichiarati nella classe |
Esempio: stampiamo tutti i costruttori della classe
import java.lang.reflect.Constructor;
System.out.println("Constructors:");
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
System.out.print(clazz.getSimpleName() + "(");
Class<?>[] params = constructor.getParameterTypes();
for (int i = 0; i < params.length; i++) {
System.out.print(params[i].getSimpleName());
if (i < params.length - 1) System.out.print(", ");
}
System.out.println(");");
}
Se nella classe è dichiarato solo il costruttore di default, verrà stampato anche quello.
4. Annotazioni: come sapere di cosa è contrassegnata una classe, un metodo o un campo
Le annotazioni sono etichette speciali che si possono applicare a classi, metodi, campi e parametri. Con la riflessione si può conoscere quali annotazioni possiede un elemento.
Ottenere le annotazioni
- Per la classe: clazz.getAnnotations()
- Per il metodo: method.getAnnotations()
- Per il campo: field.getAnnotations()
Esempio: verifichiamo la presenza dell’annotazione @Deprecated
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(Deprecated.class)) {
System.out.println(method.getName() + " is @Deprecated");
}
}
Esempio: stampiamo tutte le annotazioni della classe
for (var annotation : clazz.getAnnotations()) {
System.out.println(annotation);
}
5. Pratica: mini-programma per analizzare la struttura di una classe
Mettiamo tutto insieme e scriviamo una piccola utility che, dato il nome della classe, stampa la sua struttura: campi, metodi, costruttori e annotazioni.
Esempio di codice: ClassInspector
import java.lang.reflect.*;
import java.util.Scanner;
public class ClassInspector {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.print("Inserisci il nome completo della classe (ad esempio, java.util.ArrayList): ");
String className = scanner.nextLine();
Class<?> clazz = Class.forName(className);
System.out.println("\n== Classe: " + clazz.getName() + " ==");
// Annotazioni della classe
System.out.println("Annotazioni:");
for (Annotation annotation : clazz.getAnnotations()) {
System.out.println(" " + annotation);
}
// Campi
System.out.println("\nCampi:");
for (Field field : clazz.getDeclaredFields()) {
System.out.println(" " + Modifier.toString(field.getModifiers())
+ " " + field.getType().getSimpleName()
+ " " + field.getName());
}
// Metodi
System.out.println("\nMetodi:");
for (Method method : clazz.getDeclaredMethods()) {
System.out.print(" " + Modifier.toString(method.getModifiers())
+ " " + method.getReturnType().getSimpleName()
+ " " + method.getName() + "(");
Class<?>[] params = method.getParameterTypes();
for (int i = 0; i < params.length; i++) {
System.out.print(params[i].getSimpleName());
if (i < params.length - 1) System.out.print(", ");
}
System.out.println(");");
}
// Costruttori
System.out.println("\nCostruttori:");
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
System.out.print(" " + Modifier.toString(constructor.getModifiers())
+ " " + clazz.getSimpleName() + "(");
Class<?>[] params = constructor.getParameterTypes();
for (int i = 0; i < params.length; i++) {
System.out.print(params[i].getSimpleName());
if (i < params.length - 1) System.out.print(", ");
}
System.out.println(");");
}
}
}
Come funziona?
- L’utente inserisce il nome completo della classe (ad esempio, "java.util.ArrayList" o una propria classe).
- Il programma carica dinamicamente la classe e stampa le sue annotazioni, i campi, i metodi e i costruttori.
- Provate con le classi standard della JDK — vedrete quante cose interessanti sono nascoste dentro!
6. Dettagli utili e visualizzazione
Schema visivo: cosa si può sapere di una classe tramite riflessione
graph TD
A[Class<?>] --> B["getFields/getDeclaredFields"]
A --> C[getMethods/getDeclaredMethods]
A --> D[getConstructors/getDeclaredConstructors]
A --> E[getAnnotations]
B --> F[Field: tipo, nome, modificatori]
C --> G[Method: tipo restituito, parametri]
D --> H[Constructor: parametri]
E --> I["Annotation[]"]
Tabella: dove cercare le informazioni
| Cosa vogliamo sapere | Come ottenerlo via riflessione |
|---|---|
| Tutti i campi public | |
| Tutti i campi dichiarati | |
| Tutti i metodi public | |
| Tutti i metodi dichiarati | |
| Tutti i costruttori public | |
| Tutti i costruttori dichiarati | |
| Annotazioni della classe | |
| Annotazioni di metodo/campo | |
| Modificatori | |
7. Errori tipici nell’uso della riflessione
Errore n. 1: si confondono getFields() e getDeclaredFields().
Se cercate campi private, usate getDeclaredFields() e non getFields(). Il primo restituisce tutti i campi dichiarati nella classe, mentre il secondo — solo quelli public (e anche ereditati!).
Errore n. 2: non si gestiscono le eccezioni checked.
Molti metodi di riflessione lanciano eccezioni (ad esempio, ClassNotFoundException o SecurityException). Non dimenticate di gestirle o di dichiararle nella firma del metodo.
Errore n. 3: non si tengono in conto i modificatori di accesso.
L’accesso ai campi e ai metodi private è possibile solo dopo aver chiamato setAccessible(true), altrimenti verrà sollevata IllegalAccessException. Esempio:
Field field = clazz.getDeclaredField("age");
field.setAccessible(true); // Abilitiamo l'accesso al campo private
int value = (int) field.get(person);
Errore n. 4: ci si aspetta di vedere solo i propri metodi/campi.
getMethods() e getFields() restituiscono i membri public non solo della classe corrente, ma anche di tutti i suoi genitori, incluso Object. Questo può sorprendere se vi aspettate di vedere solo ciò che avete scritto voi.
Errore n. 5: verifica non corretta della presenza delle annotazioni.
Usate isAnnotationPresent() per controllare una specifica annotazione, invece di scorrere manualmente l’array delle annotazioni senza necessità.
GO TO FULL VERSION