Vraag Wat is reflectie en waarom is het nuttig?


Wat is reflectie en waarom is het nuttig?

Ik ben vooral geïnteresseerd in Java, maar ik ga ervan uit dat de principes in elke taal hetzelfde zijn.


1690
2017-09-01 08:39


oorsprong


antwoorden:


De naamreflectie wordt gebruikt om een ​​code te beschrijven die in staat is om andere code in hetzelfde systeem (of zichzelf) te inspecteren.

Stel dat u een object van een onbekend type in Java hebt en dat u een 'doSomething'-methode wilt gebruiken als die bestaat. Het statische typsysteem van Java is niet echt ontworpen om dit te ondersteunen, tenzij het object voldoet aan een bekende interface, maar met reflectie kan je code naar het object kijken en zien of het een methode heeft met de naam 'doSomething' en het dan noemen als je willen.

Dus, om je een code-voorbeeld hiervan in Java te geven (stel je voor dat het object in kwestie foo is):

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

Een heel gebruikelijk geval in Java is het gebruik van annotaties. JUnit 4 gebruikt bijvoorbeeld reflectie om door uw klassen te bladeren naar methoden die zijn getagd met de @Test-annotatie en roept ze vervolgens op wanneer de unit-test wordt uitgevoerd.

Er zijn enkele goede reflectievoorbeelden om mee te beginnen http://docs.oracle.com/javase/tutorial/reflect/index.html

En tenslotte, ja, de concepten komen vrijwel overeen in andere statische typen talen die reflectie ondersteunen (zoals C #). In dynamisch getypeerde talen is het hierboven beschreven gebruikscasus minder noodzakelijk (aangezien de compiler toestaat dat een methode wordt aangeroepen voor elk object, tijdens runtime mislukt als dit niet bestaat), maar het tweede geval van zoeken naar methoden die zijn gemarkeerd of gemarkeerd werk op een bepaalde manier is nog steeds gebruikelijk.

Update van een reactie:

De mogelijkheid om de code in het systeem te inspecteren en objecttypen te zien is   geen reflectie, maar Type Introspectie. Reflectie is dan het   mogelijkheid om tijdens runtime wijzigingen aan te brengen door gebruik te maken van   zelfreflectie. Het onderscheid is hier noodzakelijk als sommige talen   ondersteuning van introspectie, maar geen ondersteuning voor reflectie. Eén voorbeeld   is C ++


1414
2017-09-01 08:44



Reflectie is het vermogen van een taal om klassen, methoden, attributen, etc. tijdens runtime te inspecteren en dynamisch te callen.

Alle objecten in Java hebben bijvoorbeeld de methode getClass(), waarmee je de klasse van het object kunt bepalen, zelfs als je het niet weet tijdens het compileren (bijvoorbeeld als je het hebt opgegeven als een Object) - dit lijkt misschien triviaal, maar een dergelijke reflectie is niet mogelijk in minder dynamische talen zoals C++. Meer geavanceerd gebruik maakt het mogelijk om methodes, constructeurs, enz. Te noemen en aan te roepen.

Reflectie is belangrijk omdat je programma's kunt schrijven die niet alles hoeven te "weten" tijdens het compileren, waardoor ze dynamischer worden, omdat ze tijdens runtime bij elkaar kunnen worden gehouden. De code kan worden geschreven tegen bekende interfaces, maar de daadwerkelijke klassen die kunnen worden gebruikt, kunnen worden geïnstantieerd met reflectie van configuratiebestanden.

Veel moderne kaders gebruiken reflectie om deze reden uitgebreid. De meeste andere moderne talen gebruiken ook reflectie, en in scriptingtalen (zoals Python) zijn ze nog strakker geïntegreerd, omdat het natuurlijker aanvoelt in het algemene programmeermodel van die talen.


195
2017-09-01 08:52



Een van mijn favoriete gebruiken van reflectie is de onderstaande Java-dump-methode. Het neemt elk object als een parameter en gebruikt de Java reflection API om elke veldnaam en waarde af te drukken.

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



Gebruik van reflectie

Reflectie wordt vaak gebruikt door programma's die de mogelijkheid hebben om het runtime-gedrag van applicaties die op de virtuele Java-machine draaien te onderzoeken of aan te passen. Dit is een relatief geavanceerde functie en mag alleen worden gebruikt door ontwikkelaars die de grondbeginselen van de taal goed begrijpen. Met dat voorbehoud in gedachten is reflectie een krachtige techniek en kan het toepassingen in staat stellen operaties uit te voeren die anders onmogelijk zouden zijn.

Uitbreidingsmogelijkheden

Een toepassing kan gebruikmaken van externe, door de gebruiker gedefinieerde klassen door instanties van uitbreidbaarheidsobjecten te maken met behulp van hun volledig gekwalificeerde namen. Klassebrowsers en visuele ontwikkelomgevingen Een klassenbrowser moet de leden van klassen kunnen opsommen. Visuele ontwikkelomgevingen kunnen profiteren van het gebruik van type-informatie beschikbaar in reflectie om de ontwikkelaar te helpen bij het schrijven van de juiste code. Debuggers en testtools Debuggers moeten privé-leden in klassen kunnen onderzoeken. Testharnassen kunnen gebruikmaken van reflectie om systematisch een detecteerbare set-API's aan te roepen die in een klasse zijn gedefinieerd, om een ​​hoog niveau van codedekking in een testsuite te waarborgen.

Nadelen van reflectie

Reflectie is krachtig, maar mag niet zonder onderscheid worden gebruikt. Als het mogelijk is om een ​​bewerking uit te voeren zonder reflectie te gebruiken, dan is het beter om deze niet te gebruiken. De volgende zorgen moeten in gedachten worden gehouden bij toegang tot de code via reflectie.

  • Prestaties Overhead

Omdat reflectie typen inhoudt die dynamisch worden opgelost, kunnen bepaalde optimalisaties van de Java Virtual Machine niet worden uitgevoerd. Bijgevolg hebben reflectieve bewerkingen langzamere prestaties dan hun niet-reflecterende tegenhangers en moeten worden vermeden in delen van code die vaak worden genoemd in prestatiegevoelige toepassingen.

  • Beveiligingsbeperkingen

Reflectie vereist een runtime-toestemming die mogelijk niet aanwezig is bij een beveiligingsmanager. Dit is een belangrijke overweging voor code die moet worden uitgevoerd in een beperkte beveiligingscontext, zoals in een applet.

  • Blootstelling van Internals

Omdat reflectie het voor code mogelijk maakt operaties uit te voeren die niet-reflecterend zijn, zoals het benaderen van privévelden en -methoden, kan reflectiegebruik leiden tot onverwachte neveneffecten die code disfunctioneel kunnen maken en de draagbaarheid kunnen vernietigen. Reflecterende code breekt abstracties en kan daarom het gedrag veranderen met upgrades van het platform.

bron: The Reflection API


55
2017-10-17 11:59



Reflectie is een belangrijk mechanisme om een ​​toepassing of raamwerk te laten werken met code die misschien nog niet eens is geschreven!

Neem bijvoorbeeld uw typische web.xml-bestand. Dit bevat een lijst met servlet-elementen die geneste servlet-klasse-elementen bevatten. De servletcontainer verwerkt het bestand web.xml en maakt via reflectie een nieuw exemplaar van elke servletklasse.

Een ander voorbeeld is de Java API voor XML Parsing (JAXP). Waar een XML-parserprovider wordt 'aangesloten' via bekende systeemeigenschappen, die worden gebruikt om nieuwe instanties te construeren door middel van reflectie.

En tot slot, het meest uitgebreide voorbeeld is De lente die reflectie gebruikt om zijn bonen te maken, en voor het zware gebruik van proxies


30
2017-09-01 09:30



Niet elke taal ondersteunt reflectie, maar de principes zijn meestal hetzelfde in talen die dit ondersteunen.

Reflectie is het vermogen om te "reflecteren" op de structuur van je programma. Of concreter. Om te kijken naar de objecten en klassen die u hebt en programmatisch terugkrijgen informatie over de methoden, velden en interfaces die zij implementeren. Je kunt ook kijken naar dingen zoals annotaties.

Het is nuttig in veel situaties. Overal waar u klassen dynamisch in uw code wilt kunnen pluggen. Veel objectrelationele mappers gebruiken reflectie om objecten uit databases te kunnen instantiëren zonder van tevoren te weten welke objecten ze gaan gebruiken. Plug-in-architecturen is een andere plaats waar reflectie nuttig is. In die situaties is het belangrijk om dynamisch code te kunnen laden en te kunnen bepalen of er typen zijn die de juiste interface implementeren om als plug-in te gebruiken.


27
2017-09-01 08:50



Reflectie maakt instantiatie van nieuwe objecten, aanroep van methoden en get / set-bewerkingen op klassenvariabelen dynamisch tijdens runtime mogelijk zonder voorafgaande kennis van de implementatie.

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

In het bovenstaande voorbeeld is de null-parameter het object waarop u de methode wilt aanroepen. Als de methode statisch is, levert u null op. Als de methode niet statisch is, moet u tijdens het opstarten een geldige MyObject-instantie opgeven in plaats van null.

Reflectie geeft je ook toegang tot privéleden / methoden van een klasse:

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);
  • Voor inspectie van klassen (ook bekend als introspectie) hoeft u het reflectiepakket niet te importeren (java.lang.reflect). Metagegevens van de klas zijn toegankelijk via java.lang.Class.

Reflectie is een zeer krachtige API, maar het kan de toepassing vertragen als deze in overmaat wordt gebruikt, omdat alle typen tijdens runtime worden opgelost.


26
2017-07-08 16:12



Voorbeeld:
Neem bijvoorbeeld een externe toepassing die uw toepassing een object geeft dat u verkrijgt met behulp van hun API-methoden. Nu gebaseerd op het object dat u misschien nodig hebt om een ​​soort berekening uit te voeren.
De provider garandeert dat het object uit 3 typen kan bestaan ​​en we moeten een berekening uitvoeren op basis van welk type object.
We kunnen dus in 3 klassen implementeren die elk een andere logica bevatten. Uiteraard is de objectinformatie beschikbaar tijdens runtime, zodat je niet statisch kunt coderen om de berekening uit te voeren, daarom wordt reflectie gebruikt om het object van de klasse te instantiëren dat je nodig hebt om de berekening uit te voeren op basis van de object ontvangen van de provider.


18
2018-06-22 15:35



Java Reflection is vrij krachtig en kan erg nuttig zijn. Java Reflection maakt het mogelijk om tijdens runtime klassen, interfaces, velden en methoden te inspecteren,zonder de namen van de klassen, methoden etc. te kennen tijdens het compileren. Het is ook mogelijk om instantiëren van nieuwe objecten, methoden aanroepen en veldwaarden ophalen / instellen met behulp van reflectie.

Een snel Java Reflection-voorbeeld om u te laten zien hoe het gebruik van reflectie eruitziet:

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

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

In dit voorbeeld wordt het object Class opgehaald uit de klasse MyObject. Met behulp van het klasseobject krijgt het voorbeeld een lijst met de methoden in die klasse, worden de methoden herhaald en worden hun namen afgedrukt.

Hoe dit precies werkt, wordt hier uitgelegd

Bewerk: Na bijna 1 jaar bewerk ik dit antwoord terwijl ik tijdens het lezen over reflectie nog maar weinig gebruik van Reflectie kreeg.

  • Spring gebruikt de bonenconfiguratie zoals:


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

Wanneer de lente-context dit <bean> -element verwerkt, gebruikt het Class.forName (String) met het argument "com.example.Foo" om een ​​instantie van die klasse te maken.

Het gebruikt dan weer reflectie om de juiste setter voor het element <property> te krijgen en de waarde ervan in te stellen op de opgegeven waarde.

  • Junit gebruikt Reflection vooral voor het testen van privé- / beschermde methoden.

Voor privémethoden,

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

Voor privévelden,

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

16
2017-09-08 09:40



Volgens mijn inzicht:

Door reflectie kan de programmeur dynamisch toegang krijgen tot entiteiten in het programma. d.w.z. tijdens het coderen van een applicatie als de programmeur zich niet bewust is van een klasse of zijn werkwijzen, kan hij deze klasse dynamisch gebruiken (tijdens runtime) door gebruik te maken van reflectie.

Het wordt vaak gebruikt in scenario's waarbij de naam van een klas vaak verandert. Als een dergelijke situatie zich voordoet, is het ingewikkeld voor de programmeur om de toepassing te herschrijven en de naam van de klas keer op keer te veranderen.

In plaats daarvan moet je je door nadenken zorgen te maken over een mogelijk veranderende klasse.


12
2018-02-06 05:37



eenvoudig voorbeeld voor reflectie. In een schaakspel weet je niet wat de gebruiker tijdens runtime zal verplaatsen. reflectie kan worden gebruikt om methoden aan te roepen die al tijdens runtime zijn geïmplementeerd.

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