java reflection - Cos'è la riflessione e perché è utile?



in tutorial (17)

Cos'è la riflessione e perché è utile?

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


Answers

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, è possibile 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 classe su runtime, invocarne il metodo, ecc.

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


Java Reflection è abbastanza potente e può essere molto utile. Java Reflection rende possibile ispezionare classi, interfacce, campi e metodi in fase di esecuzione, senza conoscere i nomi delle classi, dei metodi ecc. In fase di compilazione. È anche possibile 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'oggetto Class dalla classe MyObject. Usando l'oggetto classe l'esempio ottiene una lista dei metodi in quella classe, 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:


<bean id="someID" class="com.example.Foo">
    <property name="someField" value="someValue" />
</bean>

Quando il contesto Spring elabora questo elemento <bean>, userà Class.forName (String) con l'argomento "com.example.Foo" per creare un'istanza di quella classe.

Utilizzerà nuovamente il reflection per ottenere il setter appropriato per l'elemento <property> 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);

Reflection consente l'istanziazione di nuovi oggetti, l'invocazione di metodi e le operazioni get / set sulle variabili di classe dinamicamente 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'oggetto 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 classe:

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 ). È possibile accedere ai metadati della classe 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.


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 classe e ambienti di sviluppo visivo Un browser di classe 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 classe, 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 è possibile 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 dinamicamente, non è possibile 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'autorizzazione 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


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 classe a cui appartiene un oggetto e anche i metodi di quella classe che possono essere eseguiti usando l'oggetto.
  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 classe a cui appartiene un oggetto.

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

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

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 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 ..)


La riflessione è lasciare che l'oggetto 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'oggetto di Java non può percepire come appare senza uno specchio, che è un oggetto della classe reflection, ma un oggetto in Python può percepirlo senza uno specchio. Ecco perché abbiamo bisogno di riflessione in Java.


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 oggetto 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'oggetto non sia conforme a un'interfaccia conosciuta, ma usando il reflection, il codice può guardare l'oggetto 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'oggetto 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 dinamicamente, il caso d'uso sopra descritto è meno necessario (poiché il compilatore consente di chiamare qualsiasi metodo su qualsiasi oggetto, 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 oggetto 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 dinamicamente classi, metodi, attributi, ecc. In fase di esecuzione.

Ad esempio, tutti gli oggetti in Java hanno il metodo getClass() , che consente di determinare la classe dell'oggetto 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 è possibile 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.


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


Uno dei miei usi preferiti della riflessione è il seguente metodo di dump Java. Prende qualsiasi oggetto 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();
}

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 ti dà la possibilità di scrivere più codice generico. Ti permette di creare un oggetto in runtime e chiamarne il metodo in fase di runtime. Quindi il programma può essere reso altamente parametrizzato. Permette anche di introspecting l'oggetto e la classe per rilevare le sue variabili e il metodo esposto al mondo esterno.


Secondo la mia comprensione:

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

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

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


Dalla page 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 oggetto di destinazione (in base alla sua runtime class) o membri dichiarati da una determinata classe

Include le seguenti funzionalità.

  1. Ottenere oggetti di classe,
  2. Esaminare le proprietà di una classe (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 documentation per i metodi esposti per classe.

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

Vedo notevoli inconvenienti rispetto all'uso di Reflection

Utente di Reflection:

  1. Fornisce un modo molto versatile per collegare dinamicamente 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 classe ristretta,
  3. Creazione di nuove istanze di oggetti, invocazione di metodi, acquisizione o impostazione di valori di campo di una classe 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


Java Reflection rende possibile 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, è possibile incollare il codice su un altro programma o framework che utilizza la riflessione per esaminare dinamicamente 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'oggetto che ha un metodo chiamato add . Principalmente nell'iniezione di dipendenza, l'uso della riflessione orientata all'aspetto è principalmente usato.


La migliore definizione che ho trovato finora è una di James Shore :

"Dependency Injection" è un termine di 25 dollari per un concetto di 5 centesimi. [...] Iniezione di dipendenza significa dare a un oggetto le sue variabili di istanza. [...].

C'è anche un articolo di Martin Fowler che potrebbe rivelarsi utile.

L'iniezione di dipendenza fornisce fondamentalmente gli oggetti di cui un oggetto ha bisogno (le sue dipendenze) invece di costruirli da sé. È una tecnica molto utile per i test, poiché consente alle dipendenze di essere derise o eliminate.

Le dipendenze possono essere iniettate in oggetti con molti mezzi (come iniezione del costruttore o iniezione setter). Si può persino usare strutture specializzate per l'iniezione delle dipendenze (es. Spring) per farlo, ma certamente non sono richieste. Non hai bisogno di questi quadri per avere un'iniezione di dipendenza. Istanziare e passare oggetti (dipendenze) in modo esplicito è un'iniezione altrettanto buona di un'iniezione per quadro.





java reflection terminology