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


1689
2017-09-01 08:39


origine


risposte:


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 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 tipo Introspezione. La riflessione è quindi il   possibilità di apportare modifiche in fase di esecuzione facendo uso di   introspezione. La distinzione è necessaria qui come alcune lingue   supporta l'introspezione, ma non supporta la riflessione. Uno di questi esempi   è C ++


1413
2017-09-01 08:44



Riflessione è la capacità di una lingua di ispezionare e chiamare dinamicamente classi, metodi, attributi, ecc. in fase di esecuzione.

Ad esempio, tutti gli oggetti in Java hanno il metodo getClass(), che ti consente di determinare la classe dell'oggetto anche se non lo conosci al momento della compilazione (ad es. se lo hai dichiarato come Object) - questo potrebbe sembrare banale, ma tale riflessione non è possibile in linguaggi meno dinamici come 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 delle altre lingue moderne usano anche la riflessione, e nei linguaggi di scripting (come Python) sono ancora più strettamente integrate, dal momento che è più naturale nel modello di programmazione generale di quelle lingue.


195
2017-09-01 08:52



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();
}

84
2017-09-02 16:15



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 visuale 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


55
2017-10-17 11:59



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 classe servlet nidificati. Il contenitore servlet elaborerà il file web.xml e creerà una nuova nuova istanza di ogni classe servlet tramite la reflection.

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

E infine, l'esempio più completo è Primavera che usa la riflessione per creare i suoi fagioli e per il suo uso massiccio dei proxy


30
2017-09-01 09:30



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 collegare dinamicamente 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 dinamicamente il codice e determinare se ci sono tipi che implementano l'interfaccia giusta da utilizzare come plugin è importante in quelle situazioni.


27
2017-09-01 08:50



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). I metadati di classe sono accessibili 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.


26
2017-07-08 16:12



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.


18
2018-06-22 15:35



Java Reflection è abbastanza potente e può essere molto utile. Java Reflection lo rende possibile ispezionare classi, interfacce, campi e metodi in fase di runtime,senza conoscere i nomi delle classi, i metodi ecc. al momento della compilazione. È anche possibile istanziare 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

modificare: Dopo quasi un 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);

16
2017-09-08 09:40



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.


12
2018-02-06 05:37



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);
    }

}

12
2017-11-06 04:42