Cos’è la riflessione e perché è utile?

Cos’è la riflessione e perché è utile?

Sono particolarmente interessato a Java, ma presumo che i principi siano gli stessi in qualsiasi lingua.

Il nome reflection è usato per descrivere il codice che è in grado di ispezionare altro codice nello stesso sistema (o se stesso).

Ad esempio, supponiamo di avere un object di tipo sconosciuto in Java e si desideri chiamare un metodo “doSomething” su di esso, se ne esiste uno. Il sistema di tipizzazione statico di Java non è progettato per supportare questo a meno che l’object non sia conforms a un’interfaccia conosciuta, ma usando il reflection, il codice può guardare l’object e scoprire se ha un metodo chiamato “doSomething” e quindi chiamarlo se volere.

Quindi, per darti un esempio di codice in Java (immagina che l’object in questione sia foo):

Method method = foo.getClass().getMethod("doSomething", null); method.invoke(foo, null); 

Un caso d’uso molto comune in Java è l’uso con annotazioni. Ad esempio, JUnit 4 utilizzerà la riflessione per esaminare le classi per i metodi contrassegnati con l’annotazione @Test e quindi li chiamerà durante l’esecuzione del test dell’unità.

Ci sono alcuni buoni esempi di riflessione per iniziare a http://docs.oracle.com/javase/tutorial/reflect/index.html

E infine, sì, i concetti sono praticamente simili in altri linguaggi di tipi statici che supportano la riflessione (come C #). Nei linguaggi tipizzati dynamicmente, il caso d’uso sopra descritto è meno necessario (poiché il compilatore consente di chiamare qualsiasi metodo su qualsiasi object, non funzionante in runtime se non esiste), ma il secondo caso di cercare i metodi che sono contrassegnati o lavorare in un certo modo è ancora comune.

Aggiornamento da un commento:

La possibilità di ispezionare il codice nel sistema e vedere i tipi di object non è riflessione, ma piuttosto Introspezione. La riflessione è quindi la capacità di apportare modifiche in fase di esecuzione sfruttando l’introspezione. La distinzione è necessaria in quanto alcune lingue supportano l’introspezione, ma non supportano la riflessione. Uno di questi esempi è C ++

Reflection è la capacità di una lingua di ispezionare e richiamare dynamicmente classi, metodi, attributi, ecc. In fase di esecuzione.

Ad esempio, tutti gli oggetti in Java hanno il metodo getClass() , che consente di determinare la class dell’object anche se non lo si conosce in fase di compilazione (ad esempio se lo hai dichiarato come Object ) – questo potrebbe sembrare banale, ma tale la riflessione non è ansible in linguaggi meno dinamici come il C++ . Usi più avanzati ti consentono di elencare e chiamare metodi, costruttori, ecc.

La riflessione è importante poiché consente di scrivere programmi che non devono “sapere” tutto in fase di compilazione, rendendoli più dinamici, poiché possono essere collegati insieme in fase di runtime. Il codice può essere scritto su interfacce note, ma le classi effettive da utilizzare possono essere istanziate utilizzando il reflection dai file di configurazione.

Un sacco di strutture moderne usano ampiamente la riflessione proprio per questo motivo. La maggior parte degli altri linguaggi moderni usa anche la riflessione, e nei linguaggi di scripting (come Python) sono ancora più strettamente integrati, poiché è più naturale nel modello di programmazione generale di quelle lingue.

Uno dei miei usi preferiti della riflessione è il seguente metodo di dump Java. Prende qualsiasi object come parametro e utilizza l’API Java reflection stampando ogni nome e valore di ogni campo.

 import java.lang.reflect.Array; import java.lang.reflect.Field; public static String dump(Object o, int callCount) { callCount++; StringBuffer tabs = new StringBuffer(); for (int k = 0; k < callCount; k++) { tabs.append("\t"); } StringBuffer buffer = new StringBuffer(); Class oClass = o.getClass(); if (oClass.isArray()) { buffer.append("\n"); buffer.append(tabs.toString()); buffer.append("["); for (int i = 0; i < Array.getLength(o); i++) { if (i < 0) buffer.append(","); Object value = Array.get(o, i); if (value.getClass().isPrimitive() || value.getClass() == java.lang.Long.class || value.getClass() == java.lang.String.class || value.getClass() == java.lang.Integer.class || value.getClass() == java.lang.Boolean.class ) { buffer.append(value); } else { buffer.append(dump(value, callCount)); } } buffer.append(tabs.toString()); buffer.append("]\n"); } else { buffer.append("\n"); buffer.append(tabs.toString()); buffer.append("{\n"); while (oClass != null) { Field[] fields = oClass.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { buffer.append(tabs.toString()); fields[i].setAccessible(true); buffer.append(fields[i].getName()); buffer.append("="); try { Object value = fields[i].get(o); if (value != null) { if (value.getClass().isPrimitive() || value.getClass() == java.lang.Long.class || value.getClass() == java.lang.String.class || value.getClass() == java.lang.Integer.class || value.getClass() == java.lang.Boolean.class ) { buffer.append(value); } else { buffer.append(dump(value, callCount)); } } } catch (IllegalAccessException e) { buffer.append(e.getMessage()); } buffer.append("\n"); } oClass = oClass.getSuperclass(); } buffer.append(tabs.toString()); buffer.append("}\n"); } return buffer.toString(); } 

Usi di riflessione

Reflection viene comunemente utilizzato dai programmi che richiedono la possibilità di esaminare o modificare il comportamento di runtime delle applicazioni in esecuzione nella Java virtual machine. Questa è una funzionalità relativamente avanzata e dovrebbe essere utilizzata solo da sviluppatori che hanno una conoscenza approfondita dei fondamenti della lingua. Con questo avvertimento in mente, la riflessione è una tecnica potente e può consentire alle applicazioni di eseguire operazioni che altrimenti sarebbero impossibili.

Funzionalità di estensibilità

Un’applicazione può fare uso di classi esterne definite dall’utente creando istanze di oggetti di estensibilità usando i loro nomi completi. Browser di class e ambienti di sviluppo visivo Un browser di class deve essere in grado di enumerare i membri delle classi. Gli ambienti di sviluppo visuale possono trarre vantaggio dall’utilizzo delle informazioni sul tipo disponibili in reflection per aiutare lo sviluppatore nella scrittura del codice corretto. Debugger e strumenti di test I debugger devono essere in grado di esaminare i membri privati ​​nelle classi. I cablaggi di prova possono utilizzare la riflessione per chiamare in modo sistematico un set di API individuabili definito in una class, per garantire un livello elevato di copertura del codice in una suite di test.

Inconvenienti di riflessione

La riflessione è potente, ma non dovrebbe essere usata indiscriminatamente. Se è ansible eseguire un’operazione senza utilizzare la riflessione, è preferibile evitare di utilizzarla. Le seguenti preoccupazioni dovrebbero essere tenute a mente quando si accede al codice tramite riflessione.

  • Sovraccarico delle prestazioni

Poiché la riflessione riguarda tipi risolti dynamicmente, non è ansible eseguire determinate ottimizzazioni della macchina virtuale Java. Di conseguenza, le operazioni di riflessione hanno prestazioni più lente rispetto alle controparti non riflettenti e dovrebbero essere evitate in sezioni di codice chiamate frequentemente in applicazioni sensibili alle prestazioni.

  • Restrizioni di sicurezza

Reflection richiede un’authorization di runtime che potrebbe non essere presente durante l’esecuzione in un gestore della sicurezza. Questa è una considerazione importante per il codice che deve essere eseguito in un contesto di sicurezza limitato, come in un’applet.

  • Esposizione di interni

Poiché la riflessione consente al codice di eseguire operazioni che sarebbero illegali in codice non riflettente, come l’accesso a campi e metodi privati, l’uso della riflessione può provocare effetti collaterali imprevisti, che possono rendere il codice non funzionale e distruggere la portabilità. Il codice riflettente rompe le astrazioni e quindi può modificare il comportamento con gli aggiornamenti della piattaforma.

fonte: l’API di Reflection

La riflessione è un meccanismo chiave per consentire a un’applicazione o un framework di lavorare con codice che potrebbe non essere stato ancora scritto!

Prendi ad esempio il tuo tipico file web.xml. Questo conterrà un elenco di elementi servlet, che contengono elementi di class servlet nidificati. Il contenitore servlet elaborerà il file web.xml e creerà una nuova nuova istanza di ogni class servlet tramite la reflection.

Un altro esempio potrebbe essere l’API Java per XML Parsing (JAXP) . Laddove un provider di parser XML è “plug-in” tramite proprietà di sistema ben note, che vengono utilizzate per build nuove istanze tramite la reflection.

Infine, l’esempio più completo è Spring, che usa la riflessione per creare i suoi bean e per il suo uso massiccio dei proxy

Non tutte le lingue supportano la riflessione, ma i principi sono solitamente gli stessi nelle lingue che lo supportano.

La riflessione è la capacità di “riflettere” sulla struttura del tuo programma. O più concreto. Per esaminare gli oggetti e le classi che hai e ottenere informazioni a livello di codice sui metodi, i campi e le interfacce che implementano. Puoi anche guardare cose come annotazioni.

È utile in molte situazioni. Ovunque tu voglia essere in grado di colbind dynamicmente le classi al tuo codice. I lotti di mappatori relazionali di oggetti utilizzano la riflessione per essere in grado di creare un’istanza di oggetti dai database senza sapere in anticipo quali oggetti verranno utilizzati. Le architetture plug-in sono un altro luogo in cui la riflessione è utile. Essere in grado di caricare dynamicmente il codice e determinare se ci sono tipi che implementano l’interfaccia giusta da utilizzare come plugin è importante in quelle situazioni.

Reflection consente l’istanziazione di nuovi oggetti, l’invocazione di metodi e le operazioni get / set sulle variabili di class dynamicmente in fase di esecuzione senza avere una conoscenza preliminare della sua implementazione.

 Class myObjectClass = MyObject.class; Method[] method = myObjectClass.getMethods(); //Here the method takes a string parameter if there is no param, put null. Method method = aClass.getMethod("method_name", String.class); Object returnValue = method.invoke(null, "parameter-value1"); 

Nell’esempio precedente il parametro null è l’object su cui si desidera richiamare il metodo. Se il metodo è statico, fornisci null. Se il metodo non è statico, durante il richiamo è necessario fornire un’istanza MyObject valida anziché null.

Reflection ti consente anche di accedere ai membri / metodi privati ​​di una class:

 public class A{ private String str= null; public A(String str) { this.str= str; } } 

.

 A obj= new A("Some value"); Field privateStringField = A.class.getDeclaredField("privateString"); //Turn off access check for this field privateStringField.setAccessible(true); String fieldValue = (String) privateStringField.get(obj); System.out.println("fieldValue = " + fieldValue); 
  • Per l’ispezione delle classi (noto anche come introspezione) non è necessario importare il pacchetto di riflessione ( java.lang.reflect ). È ansible accedere ai metadati della class tramite java.lang.Class .

Reflection è un’API molto potente ma può rallentare l’applicazione se utilizzata in eccesso, poiché risolve tutti i tipi in fase di esecuzione.

Esempio :
Prendi ad esempio un’applicazione remota che assegna alla tua applicazione un object che ottieni usando i loro metodi API. Ora, in base all’object, potrebbe essere necessario eseguire una sorta di calcolo.
Il provider garantisce che l’object può essere di 3 tipi e dobbiamo eseguire calcoli in base al tipo di object.
Quindi potremmo implementare in 3 classi ognuna contenente una logica diversa. Ovviamente le informazioni sull’object sono disponibili in runtime, quindi non è ansible codice statico per eseguire la computazione, quindi la riflessione viene utilizzata per istanziare l’object della class che si richiede per eseguire il calcolo basato sul object ricevuto dal fornitore.

Java Reflection è abbastanza potente e può essere molto utile. Java Reflection rende ansible ispezionare classi, interfacce, campi e metodi in fase di esecuzione, senza conoscere i nomi delle classi, dei metodi ecc. In fase di compilazione. È anche ansible creare un’istanza di nuovi oggetti, richiamare metodi e ottenere / impostare valori di campo usando la riflessione.

Un rapido esempio di Java Reflection per mostrare l’aspetto del riflesso usando:

 Method[] methods = MyObject.class.getMethods(); for(Method method : methods){ System.out.println("method = " + method.getName()); } 

Questo esempio ottiene l’object Class dalla class MyObject. Usando l’object class l’esempio ottiene una lista dei metodi in quella class, itera i metodi e stampa i loro nomi.

Esattamente come tutto questo funziona è spiegato qui

Edit : Dopo quasi 1 anno sto modificando questa risposta mentre leggendo sulla riflessione ho avuto qualche altro uso di Reflection.

  • Spring usa la configurazione del bean come:

    

Quando il contesto Spring elabora questo elemento , userà Class.forName (String) con l’argomento “com.example.Foo” per creare un’istanza di quella class.

Utilizzerà nuovamente il reflection per ottenere il setter appropriato per l’elemento e impostarne il valore sul valore specificato.

  • Junit utilizza Reflection soprattutto per testare metodi privati ​​/ protetti.

Per metodi privati,

 Method method = targetClass.getDeclaredMethod(methodName, argClasses); method.setAccessible(true); return method.invoke(targetObject, argObjects); 

Per i campi privati,

 Field field = targetClass.getDeclaredField(fieldName); field.setAccessible(true); field.set(object, value); 

Secondo la mia comprensione:

Reflection consente al programmatore di accedere dynamicmente alle quadro nel programma. Ad esempio, mentre codifica un’applicazione se il programmatore non è a conoscenza di una class o dei suoi metodi, può utilizzare tale class dynamicmente (in fase di esecuzione) utilizzando la reflection.

Viene frequentemente utilizzato in scenari in cui il nome di una class cambia frequentemente. Se si verifica una situazione del genere, è complicato per il programmatore riscrivere l’applicazione e cambiare il nome della class ancora e ancora.

Invece, usando la riflessione, è necessario preoccuparsi di un nome di class che potrebbe cambiare.

semplice esempio di riflessione. In una partita a scacchi, non sai cosa verrà spostato dall’utente in fase di esecuzione. la riflessione può essere utilizzata per chiamare metodi già implementati in fase di esecuzione.

 public class Test { public void firstMoveChoice(){ System.out.println("First Move"); } public void secondMOveChoice(){ System.out.println("Second Move"); } public void thirdMoveChoice(){ System.out.println("Third Move"); } public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { Test test = new Test(); Method[] method = test.getClass().getMethods(); //firstMoveChoice method[0].invoke(test, null); //secondMoveChoice method[1].invoke(test, null); //thirdMoveChoice method[2].invoke(test, null); } } 

Reflection è un’API che viene utilizzata per esaminare o modificare il comportamento di metodi, classi, interfacce in fase di esecuzione.

  1. Le classi richieste per la riflessione sono fornite nel java.lang.reflect package .
  2. Reflection ci fornisce informazioni sulla class a cui appartiene un object e anche i metodi di quella class che possono essere eseguiti usando l’object.
  3. Attraverso la riflessione possiamo invocare i metodi in fase di esecuzione indipendentemente dallo specifico identificatore di accesso utilizzato con essi.

I pacchetti java.lang e java.lang.reflect forniscono classi per la riflessione java.

La riflessione può essere utilizzata per ottenere informazioni su –

  1. Classe Il metodo getClass() viene utilizzato per ottenere il nome della class a cui appartiene un object.

  2. Costruttori Il metodo getConstructors() viene utilizzato per ottenere i costruttori pubblici della class a cui appartiene un object.

  3. Metodi Il metodo getMethods() viene utilizzato per ottenere i metodi pubblici della class a cui appartiene un object.

L’ API Reflection è utilizzata principalmente in:

IDE (Integrated Development Environment) ad es. Eclipse, MyEclipse, NetBeans ecc.
Debugger e strumenti di test ecc.

Vantaggi dell’utilizzo della riflessione:

Funzionalità di estensibilità: un’applicazione può utilizzare classi esterne definite dall’utente creando istanze di oggetti di estensibilità utilizzando i loro nomi completi.

Strumenti di debug e test: i debugger utilizzano la proprietà di reflection per esaminare membri privati ​​sulle classi.

svantaggi:

Sovraccarico delle prestazioni: le operazioni riflettenti hanno prestazioni più lente rispetto alle controparti non riflettenti e dovrebbero essere evitate in sezioni di codice chiamate frequentemente in applicazioni sensibili alle prestazioni.

Esposizione di interni: il codice riflettente rompe le astrazioni e quindi può modificare il comportamento con gli aggiornamenti della piattaforma.

Rif: Java Reflection javarevisited.blogspot.in

Reflection è un insieme di funzioni che ti permettono di accedere alle informazioni di runtime del tuo programma e modificarne il comportamento (con alcune limitazioni).

È utile perché consente di modificare il comportamento di runtime in base alle informazioni sulla meta del programma, ovvero, è ansible controllare il tipo di ritorno di una funzione e modificare il modo in cui gestisci la situazione.

Ad esempio in C # puoi caricare un assembly (un file .dll) in runtime, esaminarlo, navigare tra le classi e compiere azioni in base a ciò che hai trovato. Permette anche di creare un’istanza di una class su runtime, invocarne il metodo, ecc.

Dove può essere utile? Non è utile sempre ma per situazioni concrete. Ad esempio, è ansible utilizzarlo per ottenere il nome della class ai fini della registrazione, per creare dynamicmente gestori per gli eventi in base a quanto specificato in un file di configurazione e così via …

Voglio solo aggiungere un punto a tutto ciò che è stato elencato.

Con Reflection API puoi scrivere il metodo universale toString() per qualsiasi object.

È utile al debug.

Ecco alcuni esempi:

 class ObjectAnalyzer { private ArrayList visited = new ArrayList(); /** * Converts an object to a string representation that lists all fields. * @param obj an object * @return a string with the object's class name and all field names and * values */ public String toString(Object obj) { if (obj == null) return "null"; if (visited.contains(obj)) return "..."; visited.add(obj); Class cl = obj.getClass(); if (cl == String.class) return (String) obj; if (cl.isArray()) { String r = cl.getComponentType() + "[]{"; for (int i = 0; i < Array.getLength(obj); i++) { if (i > 0) r += ","; Object val = Array.get(obj, i); if (cl.getComponentType().isPrimitive()) r += val; else r += toString(val); } return r + "}"; } String r = cl.getName(); // inspect the fields of this class and all superclasss do { r += "["; Field[] fields = cl.getDeclaredFields(); AccessibleObject.setAccessible(fields, true); // get the names and values of all fields for (Field f : fields) { if (!Modifier.isStatic(f.getModifiers())) { if (!r.endsWith("[")) r += ","; r += f.getName() + "="; try { Class t = f.getType(); Object val = f.get(obj); if (t.isPrimitive()) r += val; else r += toString(val); } catch (Exception e) { e.printStackTrace(); } } } r += "]"; cl = cl.getSuperclass(); } while (cl != null); return r; } } 

Dalla pagina di documentazione di java

java.lang.reflect pacchetto java.lang.reflect fornisce classi e interfacce per ottenere informazioni riflessive su classi e oggetti. Reflection consente l’accesso programmatico alle informazioni sui campi, i metodi e i costruttori delle classi caricate e l’uso di campi, metodi e costruttori riflessi per operare sulle rispettive controparti sottostanti, entro i limiti di sicurezza.

AccessibleObject consente la soppressione dei controlli di accesso se è disponibile ReflectPermission necessario.

Le classi in questo pacchetto, insieme a java.lang.Class , includono applicazioni come debugger, interpreti, ispettori di oggetti, browser di classi e servizi come Object Serialization e JavaBeans che richiedono l’accesso ai membri pubblici di un object di destinazione (in base alla sua runtime class) o membri dichiarati da una determinata class

Include le seguenti funzionalità.

  1. Ottenere oggetti di class,
  2. Esaminare le proprietà di una class (campi, metodi, costruttori),
  3. Impostare e ottenere valori di campo,
  4. Invocare metodi,
  5. Creare nuove istanze di oggetti.

Dai un’occhiata a questo link alla documentazione per i metodi esposti per class.

Da questo articolo (di Dennis Sosnoski, Presidente, Sosnoski Software Solutions, Inc) e questo articolo (pdf di sicurezza-esplorazioni):

Vedo notevoli inconvenienti rispetto all’uso di Reflection

Utente di Reflection:

  1. Fornisce un modo molto versatile per colbind dynamicmente i componenti del programma
  2. È utile per creare librerie che funzionano con oggetti in modi molto generali

Svantaggi della riflessione:

  1. Reflection è molto più lento del codice diretto quando utilizzato per l’accesso al campo e al metodo.
  2. Può oscurare ciò che sta effettivamente accadendo all’interno del tuo codice
  3. Ignora il codice sorgente può creare problemi di manutenzione
  4. Il codice di riflessione è anche più complesso del corrispondente codice diretto
  5. Consente la violazione dei principali vincoli di sicurezza Java come la protezione dell’accesso ai dati e la sicurezza dei tipi

Abusi generali:

  1. Caricamento di classi riservate,
  2. Ottenere riferimenti a costruttori, metodi o campi di una class ristretta,
  3. Creazione di nuove istanze di oggetti, invocazione di metodi, acquisizione o impostazione di valori di campo di una class limitata.

Dai un’occhiata a questa domanda SE riguardo l’abuso della funzione di riflessione:

Come leggo un campo privato in Java?

Sommario:

Anche l’uso insicuro delle sue funzioni da un codice di sistema può facilmente portare al compromesso di una modalità di sicurezza Java l. Quindi usa questa funzione con parsimonia

Come suggerisce il nome stesso, esso riflette ciò che contiene, ad esempio il metodo di class, ecc. Oltre a fornire funzionalità per invocare il metodo creando istanze dynamicmente in fase di runtime.

È usato da molti framework e applicazioni sotto il legno per invocare servizi senza conoscere realmente il codice.

La riflessione è lasciare che l’object veda il loro aspetto. Questo argomento non sembra avere nulla a che fare con la riflessione. In effetti, questa è l’abilità di “autoidentificazione”.

La riflessione stessa è una parola per quei linguaggi che mancano della capacità di auto-conoscenza e auto-sensing come Java e C #. Poiché non hanno la capacità di auto-conoscenza, quando vogliamo osservare come appare, dobbiamo avere un’altra cosa su cui riflettere. I linguaggi dinamici eccellenti come Ruby e Python possono percepire il proprio riflesso senza l’aiuto di altri individui. Possiamo dire che l’object di Java non può percepire come appare senza uno specchio, che è un object della class reflection, ma un object in Python può percepirlo senza uno specchio. Ecco perché abbiamo bisogno di riflessione in Java.

Reflection ha molti usi . Quello con cui ho più familiarità è riuscire a creare codice al volo.

IE: classi dinamiche, funzioni, costruttori – basati su qualsiasi dato (xml / array / sql results / hardcoded / etc ..)

Reflection ti dà la possibilità di scrivere più codice generico. Ti permette di creare un object in runtime e chiamarne il metodo in fase di runtime. Quindi il programma può essere reso altamente parametrizzato. Permette anche di introspecting l’object e la class per rilevare le sue variabili e il metodo esposto al mondo esterno.

Java Reflection rende ansible ispezionare classi, interfacce, campi e metodi in fase di esecuzione, senza conoscere i nomi delle classi, dei metodi ecc. In fase di compilazione. Principalmente a livello di struttura si possono ottenere i massimi benefici della riflessione. Il codice byte che viene compilato se necessita di ulteriori modifiche in fase di esecuzione per l’esame, la modifica, l’aggiunta di più codice byte in sé o un altro programma o un altro framework a livello di metodo, livello variabile di istanza, livello costruttore, riflessione livello annotazione può essere utile.

Supponiamo di avere un metodo add(Int a,int b) . Il codice byte equivalente è supponiamo B1 . Se supponiamo di avere 1000 metodi chiamati add nel tuo sistema. Ora si desidera controllare il valore dei parametri a e b prima che venga chiamato il metodo add . Pertanto, è ansible incollare il codice su un altro programma o framework che utilizza la riflessione per esaminare dynamicmente il valore del codice byte utilizzando Object.getClass.getMethod() . Ci sono diverse classi da esaminare. Può aggiungere più operazioni prima che venga chiamato il metodo add . Ma il programma stesso o un altro programma o framework non conoscono l’object che ha un metodo chiamato add . Principalmente nell’iniezione di dipendenza, l’uso della riflessione orientata all’aspetto è principalmente usato.