Che cos'è la riflessione e perché è utile?


2124

Che cos'è la riflessione e perché è utile?

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


9
Per me è un modo per ottenere nomi di classe in fase di esecuzione e creare oggetti di quella classe.
Nabin,

65
perché questa è una domanda popolare che vorrei sottolineare che la riflessione (senza annotazioni) dovrebbe essere l'ultimo strumento a cui vai per risolvere un problema. Lo uso e lo adoro, ma annulla tutti i vantaggi della tipizzazione statica di Java. Se ne hai bisogno, isolalo in un'area il più piccola possibile (un metodo o una classe). È più accettabile usarlo nei test rispetto al codice di produzione. Con le annotazioni dovrebbe andare bene - Il punto principale non è specificare i nomi di classe o metodo come "Stringhe" se è possibile evitarlo.
Bill K,


4
Oltre al commento di @ BillK: la riflessione è molto potente, la definirei magica. Con un grande potere viene una grande responsabilità. Usalo solo se sai cosa stai facendo.
MC Emperor

Puoi evitare molte delle insidie ​​coinvolte nella riflessione usando Collettore @Jailbreak. Fornisce un accesso diretto e sicuro ai tipi , ai campi privati, ai metodi, ecc. Lascia che il compilatore Java verifichi in modo sicuro il tuo codice e consenti a Manifold di generare l'accesso di riflessione sottostante per te. Ulteriori informazioni: manifold.systems/docs.html#type-safe-reflection
Scott,

Risposte:


1716

La riflessione del nome viene utilizzata per descrivere il codice che è in grado di ispezionare altri codici nello stesso sistema (o stesso).

Ad esempio, supponiamo di avere un oggetto di tipo sconosciuto in Java e che si desideri chiamare un metodo 'doSomething' se ne esiste uno. Il sistema di tipizzazione statica di Java non è realmente progettato per supportare questo a meno che l'oggetto non sia conforme a un'interfaccia nota, ma usando la riflessione, il tuo 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 le annotazioni. JUnit 4, ad esempio, utilizzerà la reflection per esaminare le classi per i metodi contrassegnati con l'annotazione @Test e quindi li chiamerà durante l'esecuzione del test unitario.

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

E infine, sì, i concetti sono praticamente simili in altri linguaggi tipicamente statici che supportano la riflessione (come C #). Nei linguaggi tipizzati dinamicamente, il caso d'uso sopra descritto è meno necessario (poiché il compilatore consentirà di chiamare qualsiasi metodo su qualsiasi oggetto, fallendo in fase di esecuzione se non esiste), ma il secondo caso di ricerca di metodi contrassegnati o lavorare in un certo modo è ancora comune.

Aggiornamento da un commento:

La capacità di ispezionare il codice nel sistema e vedere i tipi di oggetto non è riflessione, ma piuttosto Tipo Introspezione. La riflessione è quindi la capacità di apportare modifiche in fase di esecuzione facendo uso dell'introspezione. La distinzione è necessaria qui poiché alcune lingue supportano l'introspezione, ma non supportano la riflessione. Uno di questi esempi è C ++


32
puoi spiegare qual è il significato di quel parametro null in questa riga Metodo method = foo.getClass (). getMethod ("doSomething", null);
Krishna Chaitanya il

54
Il valore null indica che non ci sono parametri passati al metodo foo. Vedi docs.oracle.com/javase/6/docs/api/java/lang/reflect/… , java.lang.Object ...) per i dettagli.
Matt Sheppard,

791
Solo per chiarire dal momento che questo ha così tanti voti. La capacità di ispezionare il codice nel sistema e vedere i tipi di oggetto non è riflessione, ma piuttosto Tipo Introspezione. La riflessione è quindi la capacità di apportare modifiche in fase di esecuzione facendo uso dell'introspezione. La distinzione è necessaria qui poiché alcune lingue supportano l'introspezione, ma non supportano la riflessione. Uno di questi esempi è C ++.
bigtunacan,

39
Adoro la riflessione, ma se hai il controllo del codice, usare la riflessione come specificato in questa risposta non è consono e quindi un abuso - Dovresti usare Type Introspection (istanza di) e tipi forti. Se non c'è altro modo che riflessione per fare qualcosa, è così che dovrebbe essere fatto. La riflessione provoca grave angoscia perché perdi tutti i vantaggi dell'uso di un linguaggio tipicamente statico. Se ne hai bisogno, ne hai bisogno, tuttavia anche allora prenderei in considerazione una soluzione preconfezionata come Spring o qualcosa che racchiuda completamente la riflessione necessaria - IE: lascia che qualcun altro abbia il mal di testa.
Bill K,

6
@bigtunacan Da dove hai preso quelle informazioni? Vedo il termine "riflesso" utilizzato nella documentazione Java ufficiale di Oracle per descrivere non solo la capacità di apportare modifiche in fase di esecuzione, ma anche la capacità di vedere il tipo di un oggetto. Senza contare che la maggior parte cosiddetta "tipo di introspezione" classi correlate (es: Method, Constructor, Modifier, Field, Member, in fondo a quanto pare tutto tranne Class) sono all'interno della java.lang.*reflect*confezione. Forse il concetto di "riflessione" comprende in modo esaustivo sia "l'introspezione del tipo" sia la modifica in fase di esecuzione?
RestInPeace,

246

La riflessione è la capacità di un linguaggio 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 esempio 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 in quanto consente di scrivere programmi che non devono "conoscere" tutto in fase di compilazione, rendendoli più dinamici, poiché possono essere collegati insieme in fase di esecuzione. Il codice può essere scritto su interfacce note, ma le classi effettive da utilizzare possono essere istanziate usando la riflessione dai file di configurazione.

Molti quadri moderni 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, dal momento che si sente più naturale nel modello generale di programmazione di quei linguaggi.


2
Quindi, in altre parole, puoi creare un'istanza dal suo nome qualificato e il compilatore non se ne lamenterà (perché supponi di usare solo una stringa per il nome della classe). Quindi, in fase di esecuzione, se quella classe non è presente, si ottiene un'eccezione. In questo caso hai in qualche modo ignorato il compilatore. Mi daresti un caso d'uso specifico per questo? Non riesco a immaginare quando lo sceglierei.
Fernando Gabrieli,

3
@FernandoGabrieli mentre è vero che è facile creare errori di runtime con reflection, è anche perfettamente possibile utilizzare reflection senza rischiare eccezioni di runtime. Come accennato nella mia risposta, un uso comune di reflection è per librerie o framework, che esplicitamente non possono conoscere la struttura dell'applicazione al momento della compilazione, poiché sono compilati separatamente dall'applicazione. Qualsiasi libreria che utilizza "codice per convenzione" è probabile che utilizzi la riflessione, ma non necessariamente utilizza stringhe magiche.
Liedman,

1
C++ha informazioni sul tipo di runtime. RTTI
Ayxan,

114

Uno dei miei usi preferiti di reflection è il seguente metodo di dump Java. Prende qualsiasi oggetto come parametro e utilizza l'API di riflessione Java per stampare ogni nome e valore di 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();
}

8
Cosa dovrebbe essere impostato su Callcount?
Tom,

8
Ho ricevuto un'eccezione nel thread "AWT-EventQueue-0" java.lang.StackOverflowError quando ho eseguito questo.
Tom,

3
@ Tom callCountdovrebbe essere impostato su zero. Il suo valore viene utilizzato per determinare quante schede devono precedere ciascuna riga di output: ogni volta che il dump deve eseguire il dump di un "oggetto secondario", l'output verrà stampato come nidificato nel padre. Questo metodo si rivela utile quando è avvolto in un altro. Prendere in considerazione printDump(Object obj){ System.out.println(dump(obj, 0)); }.
fny

1
Il java.lang.StackOverflowError potrebbe essere creato in caso di riferimenti circolari, a causa della ricorsione non selezionata: buffer.append (dump (valore, callCount))
Arnaud P

4
Puoi rilasciare specificamente il tuo codice su dominio pubblico, per favore?
stolsvik,

84

Usi della riflessione

Reflection è comunemente utilizzato da programmi che richiedono la capacità di esaminare o modificare il comportamento di runtime delle applicazioni in esecuzione nella macchina virtuale Java. Questa è una funzionalità relativamente avanzata e dovrebbe essere utilizzata solo dagli sviluppatori che hanno una buona conoscenza 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.

Caratteristiche di estensibilità

Un'applicazione può utilizzare 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 visivo possono trarre vantaggio dall'utilizzo delle informazioni sul tipo disponibili in riflessione per aiutare lo sviluppatore a scrivere il codice corretto. Debugger e strumenti di test I debugger devono essere in grado di esaminare i membri privati ​​nelle classi. I cablaggi di test possono utilizzare la riflessione per chiamare sistematicamente un set di API rilevabili definito in una classe, per garantire un elevato livello di copertura del codice in una suite di test.

Svantaggi della riflessione

La riflessione è potente, ma non dovrebbe essere usata indiscriminatamente. Se è possibile eseguire un'operazione senza usare la riflessione, è preferibile evitare di usarla. Le seguenti preoccupazioni devono essere tenute presenti quando si accede al codice tramite reflection.

  • Prestazioni generali

Poiché la riflessione implica tipi che vengono risolti dinamicamente, alcune ottimizzazioni della macchina virtuale Java non possono essere eseguite. Di conseguenza, le operazioni riflettenti hanno prestazioni più lente rispetto alle loro controparti non riflettenti e dovrebbero essere evitate in sezioni di codice che vengono chiamate frequentemente in applicazioni sensibili alle prestazioni.

  • Limitazioni di sicurezza

Reflection richiede un'autorizzazione di runtime che potrebbe non essere presente durante l'esecuzione in un gestore della sicurezza. Ciò è importante per il codice che deve essere eseguito in un contesto di sicurezza limitato, come in un'applet.

  • Esposizione degli interni

Poiché reflection consente al codice di eseguire operazioni che sarebbero illegali nel codice non riflettente, come l'accesso a campi e metodi privati, l'uso di reflection può comportare effetti collaterali imprevisti, che possono rendere il codice disfunzionale e possono distruggere la portabilità. Il codice riflettente rompe le astrazioni e quindi può cambiare comportamento con gli aggiornamenti della piattaforma.

fonte: API Reflection


44

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

Un altro esempio potrebbe essere l'API Java per XML Parsing (JAXP) . Laddove un provider di parser XML sia "collegato" tramite proprietà di sistema ben note, utilizzate per costruire nuove istanze tramite la riflessione.

E infine, l'esempio più completo è la primavera che usa la riflessione per creare i suoi bean e per il suo uso intenso di proxy


36

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

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

È utile in molte situazioni. Ovunque tu voglia essere in grado di collegare dinamicamente le classi al tuo codice. Molti mappatori relazionali di oggetti usano la riflessione per essere in grado di istanziare oggetti dai database senza sapere in anticipo quali oggetti useranno. 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 la giusta interfaccia da usare come plugin è importante in quelle situazioni.


Devo creare un'istanza degli oggetti in base ai dati presenti nel DB. Credo che questo sia ciò che stai dicendo. Il codice di esempio mi aiuterebbe molto. Grazie in anticipo.
Atom,

34

Reflection consente di creare istanze di nuovi oggetti, invocare metodi e ottenere / impostare operazioni su variabili di classe in modo dinamico 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 sopra il parametro null è l'oggetto su cui si desidera invocare il metodo. Se il metodo è statico, fornire null. Se il metodo non è statico, durante la chiamata è necessario fornire un'istanza MyObject valida anziché null.

Reflection ti consente anche di accedere a 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 (nota 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.


21

Java Reflection è abbastanza potente e può essere molto utile. Java Reflection consente di 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, invocare metodi e ottenere / impostare valori di campo mediante la riflessione.

Un rapido esempio di Java Reflection per mostrarti come appare l'utilizzo di reflection:

Method[] methods = MyObject.class.getMethods();

    for(Method method : methods){
        System.out.println("method = " + method.getName());
    }

Questo esempio ottiene l'oggetto Class dalla classe denominata MyObject. Utilizzando l'oggetto class l'esempio ottiene un elenco dei metodi in quella classe, itera i metodi e stampa i loro nomi.

Il modo esatto in cui funziona tutto questo è spiegato qui

Modifica : dopo quasi 1 anno sto modificando questa risposta, mentre leggendo la riflessione ho avuto qualche altro utilizzo di Reflection.

  • Spring utilizza una configurazione bean come:


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

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

Quindi utilizzerà nuovamente la riflessione per ottenere il setter appropriato per l'elemento <proprietà> e impostarne il valore sul valore specificato.

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

Per i metodi privati,

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

Per campi privati,

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

21

Esempio:

Prendi ad esempio un'applicazione remota che dà 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 fornitore garantisce che l'oggetto può essere di 3 tipi e dobbiamo eseguire il calcolo 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 codificare staticamente per eseguire il calcolo, quindi la riflessione viene utilizzata per creare un'istanza dell'oggetto della classe richiesta per eseguire il calcolo in base al oggetto ricevuto dal provider.


Ho bisogno di qualcosa di simile .. Un esempio mi aiuterebbe molto perché sono nuovo ai concetti di riflessione ..
Atom

2
Sono confuso: non puoi usare instanceofper determinare il tipo di oggetto in fase di esecuzione?
ndm13,

19

Semplice esempio di riflessione. In una partita a scacchi, non sai cosa verrà spostato dall'utente in fase di esecuzione. reflection può essere utilizzato 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);
    }

}

18

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 sotto 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 metodi in fase di esecuzione indipendentemente dall'identificatore di accesso utilizzato con essi.

I pacchetti java.lange java.lang.reflectforniscono classi per la riflessione java.

Reflection può essere utilizzato per ottenere informazioni su -

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

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

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

L' API di Reflection viene utilizzata principalmente in:

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

Vantaggi dell'utilizzo di Reflection:

Caratteristiche 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: debugger utilizzano la proprietà di reflection per esaminare i membri privati ​​delle classi.

svantaggi:

Prestazioni generali: operazioni riflettenti hanno prestazioni più lente rispetto alle loro controparti non riflettenti e dovrebbero essere evitate in sezioni di codice che vengono chiamate frequentemente in applicazioni sensibili alle prestazioni.

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

Rif: Java Reflection javarevisited.blogspot.in


4
Aggiungerei agli svantaggi " Si rompe il refactoring ". Per me questo è il motivo principale per evitare il più possibile la riflessione.
SantiBailors,

Quindi ci permette (per esempio) di ispezionare le classi che abbiamo (se ne abbiamo delle istanze o no), giusto? Con questo intendo, ottenere i loro metodi o costruttori e usarli per creare nuove istanze / invocarle. Perché diciamo "cambiare il comportamento del programma" se il comportamento è già presente ma con codice diverso? Perché si chiama "riflesso"? Grazie
Fernando Gabrieli,

15

Secondo la mia comprensione:

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

Viene spesso 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 modificare il nome della classe ancora e ancora.

Invece, usando la riflessione, è necessario preoccuparsi di un possibile cambio di nome della classe.


15

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

È utile perché ti consente di modificare il comportamento in fase di esecuzione in base alle meta informazioni del tuo programma, ovvero puoi controllare il tipo di ritorno di una funzione e cambiare il modo in cui gestisci la situazione.

In C #, ad esempio, puoi caricare un assembly (un .dll) in fase di esecuzione ed esaminarlo, navigando attraverso le classi e eseguendo le azioni in base a ciò che hai trovato. Ti consente anche di creare un'istanza di una classe in fase di runtime, invocarne il metodo, ecc.

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


11

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

Con l' API Reflection puoi scrivere un toString()metodo universale per qualsiasi oggetto.

È utile per il debug.

Ecco qualche esempio:

class ObjectAnalyzer {

   private ArrayList<Object> visited = new ArrayList<Object>();

   /**
    * 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 superclasses
      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;
   }    
}

11

La riflessione è di lasciare che l'oggetto veda il loro aspetto. Questo argomento sembra non avere nulla a che fare con la riflessione. In realtà, questa è l'abilità di "autoidentificazione".

Riflessione stessa è una parola per quei linguaggi che mancano della capacità di conoscenza di sé e di auto-percezione come Java e C #. Poiché non hanno la capacità di conoscere se stessi, quando vogliamo osservare come appare, dobbiamo avere un'altra cosa da riflettere su come appare. Ottimi linguaggi dinamici 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 di riflessione, ma un oggetto in Python può percepirlo senza uno specchio. Ecco perché abbiamo bisogno di riflessione in Java.


type (), isinstance (), callable (), dir () e getattr (). .... queste sono chiamate di riflessione pitonica
AnthonyJClink

9

Dalla pagina della documentazione di Java

java.lang.reflectIl pacchetto fornisce classi e interfacce per ottenere informazioni riflessive su classi e oggetti. Reflection consente l'accesso a livello di codice a informazioni su campi, metodi e costruttori di classi caricate e l'uso di campi, metodi e costruttori riflessi per operare sulle loro controparti sottostanti, entro limiti di sicurezza.

AccessibleObjectconsente la soppressione dei controlli di accesso se ReflectPermissionè necessario .

Le classi di questo pacchetto, insieme ad java.lang.Classospitare applicazioni come debugger, interpreti, ispettori di oggetti, browser di classi e servizi come Object Serializatione JavaBeansche hanno bisogno di accedere ai membri pubblici di un oggetto di destinazione (basato sulla sua classe di runtime) o ai membri dichiarati da una determinata classe

Include le seguenti funzionalità.

  1. Ottenere oggetti di classe,
  2. Esame delle proprietà di una classe (campi, metodi, costruttori),
  3. Impostazione e acquisizione di valori di campo,
  4. Metodi di invocazione,
  5. Creazione di nuove istanze di oggetti.

Dai un'occhiata a questo link di documentazione per i metodi esposti per Classclasse.

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

Vedo notevoli svantaggi rispetto all'uso di Reflection

Utente di Reflection:

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

Svantaggi della riflessione:

  1. La riflessione è molto più lenta del codice diretto quando viene utilizzata per l'accesso al campo e al metodo.
  2. Può oscurare ciò che sta realmente 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 codice diretto corrispondente
  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 riservata,
  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 relativa alla funzione di abuso di riflessione:

Come posso leggere un campo privato in Java?

Sommario:

L'uso non sicuro delle sue funzioni condotte all'interno di un codice di sistema può anche portare facilmente al compromesso di una modalità di sicurezza Java l. Quindi usa questa funzione con parsimonia


Un modo per evitare i problemi di prestazione di Reflection in alcuni casi è di far Woozleesaminare una classe ad altre classi all'avvio per vedere quali hanno un RegisterAsWoozleHelper()metodo statico e invocare tutti quei metodi che trova con un callback che possono usare per raccontarsi Woozle, evitando è necessario utilizzare Reflection, ad es. deserializzare i dati.
supercat

9

Come suggerisce il nome stesso, riflette ciò che contiene, ad esempio, il metodo di classe, ecc. Oltre a fornire funzionalità per invocare il metodo che crea l'istanza in modo dinamico in fase di esecuzione.

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


7

Reflection ti dà la possibilità di scrivere codice più generico. Ti consente di creare un oggetto in fase di esecuzione e di chiamarne il metodo in fase di esecuzione. Quindi il programma può essere reso altamente parametrizzato. Permette inoltre all'introspezione dell'oggetto e della classe di rilevare le sue variabili e il metodo esposto al mondo esterno.


6

Reflectionha molti usi . Quello con cui ho più familiarità è di poter creare il codice al volo.

IE: classi dinamiche, funzioni, costruttori - in base a qualsiasi dato (risultati xml / array / sql / hardcoded / ecc.)


1
Questa risposta sarebbe molto migliore se
fornissi

Nessun problema. Ho usato la riflessione in un'applicazione Windows che genera dinamicamente l'interfaccia basata su XML preso da un database.
Ess Kay,

Quindi, in sostanza, esiste una classe che ho creato che mostra all'utente un rapporto. Questo rapporto ha parametri come Data (da a) id o qualsiasi altra cosa. Queste informazioni sono archiviate in xml. Quindi prima abbiamo una selezione di report. Sulla base del rapporto selezionato, il modulo ottiene l'xml. Una volta recuperato l'xml, utilizza la riflessione per creare una classe con campi basati sui tipi riflessi. Dopo essere passati a un altro report, l'ardesia viene cancellata e vengono generati nuovi campi basati sull'xml. Quindi è essenzialmente una forma dinamica basata sulla riflessione. L'ho usato anche in altri modi, ma questo dovrebbe essere una speranza sufficiente che aiuta
Ess Kay,

3

Voglio rispondere a questa domanda con l'esempio. Innanzitutto il Hibernateprogetto utilizza Reflection APIper generare CRUDistruzioni per colmare il divario tra l'applicazione in esecuzione e l'archivio di persistenza. Quando le cose cambiano nel dominio, Hibernateè necessario conoscerle per conservarle nell'archivio dati e viceversa.

In alternativa funziona Lombok Project. Inietta semplicemente il codice al momento della compilazione, provocando l'inserimento del codice nelle classi del dominio. (Penso che sia OK per getter e setter)

Hibernatescelto reflectionperché ha un impatto minimo sul processo di compilazione di un'applicazione.

E da Java 7 abbiamo MethodHandles, che funziona come Reflection API. Nei progetti, per lavorare con i logger è sufficiente copiare e incollare il codice seguente:

Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());

Perché in questo caso è difficile fare errori di battitura.


3

Come trovo sia meglio spiegare con l'esempio e nessuna delle risposte sembra farlo ...

Un esempio pratico di utilizzo delle riflessioni sarebbe un Java Language Server scritto in Java o un PHP Language Server scritto in PHP, ecc. Language Server offre le tue capacità IDE come completamento automatico, salto alla definizione, guida contestuale, tipi di suggerimento e altro ancora. Per avere tutti i nomi dei tag (parole che possono essere completate automaticamente) per mostrare tutte le possibili corrispondenze durante la digitazione, il Language Server deve ispezionare tutto ciò che riguarda la classe compresi i blocchi doc e i membri privati. Per questo ha bisogno di un riflesso di detta classe.

Un esempio diverso sarebbe un test unitario di un metodo privato. Un modo per farlo è quello di creare una riflessione e cambiare l'ambito del metodo in pubblico nella fase di impostazione del test. Naturalmente si può sostenere che i metodi privati ​​non dovrebbero essere testati direttamente, ma non è questo il punto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.