Pytanie Czym jest refleksja i dlaczego jest przydatna?


Czym jest refleksja i dlaczego jest przydatna?

Szczególnie interesuje mnie Java, ale zakładam, że zasady są takie same w każdym języku.


1696
2017-09-01 08:39


pochodzenie


Dla mnie jest to sposób na uzyskanie nazw klas w czasie wykonywania i tworzenie obiektów tej klasy. - Nabin
ponieważ jest to popularne pytanie, chciałbym podkreślić, że odbicie (bez adnotacji) powinno być ostatnim narzędziem, do którego przyszło się rozwiązać. Używam go i uwielbiam, ale eliminuje wszystkie zalety pisania statycznego Javy. Jeśli go potrzebujesz, izoluj go na możliwie najmniejszy obszar (jedna metoda lub jedna klasa). Bardziej do zaakceptowania jest używanie go w testach niż kod produkcyjny. Z adnotacjami powinno być dobrze - Głównym celem nie jest określenie nazw klas lub metod jako "Strings", jeśli możesz tego uniknąć. - Bill K
Zobacz też: softwareengineering.stackexchange.com/questions/123956/... - givanse


Odpowiedzi:


Odbicie nazwy służy do opisu kodu, który jest w stanie sprawdzić inny kod w tym samym systemie (lub w sobie).

Na przykład, powiedzmy, że masz obiekt o nieznanym typie w Javie i chciałbyś wywołać na nim metodę "doSomething", jeśli taka istnieje. System statycznego wpisywania języka Java nie jest tak naprawdę zaprojektowany, aby to obsługiwać, chyba że obiekt jest zgodny ze znanym interfejsem, ale za pomocą odbicia Twój kod może spojrzeć na obiekt i sprawdzić, czy ma on metodę o nazwie "doSomething", a następnie wywołać ją, jeśli chcieć.

Tak więc, aby dać ci przykład kodu w języku Java (wyobraź sobie, że dany obiekt to foo):

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

Jednym z bardzo typowych przypadków użycia w Javie jest użycie z adnotacjami. JUnit 4, na przykład, użyje refleksji do przejrzenia twoich klas dla metod oznaczonych adnotacją @Test, a następnie wywoła je podczas uruchamiania testu jednostkowego.

Oto kilka przykładów dobrych refleksji na początek http://docs.oracle.com/javase/tutorial/reflect/index.html

I na koniec, tak, pojęcia są prawie podobne w innych statycznych typach języków, które obsługują odbicie (jak C #). W językach pisanych dynamicznie opisany przypadek użycia jest mniej konieczny (ponieważ kompilator zezwala na wywoływanie dowolnej metody na dowolnym obiekcie, aw przypadku awarii wystąpi awaria, jeśli nie istnieje), ale drugi przypadek poszukiwania metod, które są zaznaczone lub praca w pewien sposób jest nadal powszechna.

Zaktualizuj z komentarza:

Możliwość sprawdzenia kodu w systemie i zobaczenia typów obiektów jest   nie odbicie, ale raczej typ introspekcji. Odbicie jest wtedy   możliwość modyfikowania w czasie wykonywania przez użycie   introspekcja. Rozróżnienie to jest konieczne w niektórych językach   wspierają introspekcję, ale nie wspierają refleksji. Jeden taki przykład   jest C ++


1417
2017-09-01 08:44



możesz wyjaśnić, jakie jest znaczenie tego parametru zerowego w tym wierszu Metoda method = foo.getClass (). getMethod ("doSomething", null); - Krsna Chaitanya
Wartość null wskazuje, że żadne parametry nie są przekazywane do metody foo. Widzieć docs.oracle.com/javase/6/docs/api/java/lang/reflect/..., java.lang.Object ...) po szczegóły. - Matt Sheppard
Żeby to wyjaśnić, skoro ma tak wiele przebojów. Możliwość sprawdzenia kodu w systemie i zobaczenia typów obiektów nie jest odbiciem, lecz raczej introspekcją typu. Refleksja jest wtedy zdolnością do dokonywania modyfikacji w czasie wykonywania, za pomocą introspekcji. Rozróżnienie jest konieczne, ponieważ niektóre języki wspierają introspekcję, ale nie wspierają refleksji. Jednym z takich przykładów jest C ++. - bigtunacan
Uwielbiam refleksję, ale jeśli masz kontrolę nad kodem, użycie refleksji określonej w tej odpowiedzi jest nieważne i dlatego jest nadużyciem - powinieneś używać typu introspekcji (instanceof) i silnych typów. Jeśli jest jakikolwiek sposób, ale refleksja, aby coś zrobić, tak należy to zrobić. Refleksja powoduje poważne bóle serca, ponieważ tracisz wszystkie zalety używania statycznie napisanego języka. Jeśli jej potrzebujesz, to potrzebujesz, ale nawet wtedy rozważałbym gotowe rozwiązanie, takie jak Wiosna, czy coś, co całkowicie zamyka odbicie potrzebne - IE: pozwól komuś innemu mieć bóle głowy. - Bill K
@bigtunacan Skąd masz te informacje? Postrzegam termin "odbicie" używany w oficjalnej dokumentacji Java od Oracle, aby opisać nie tylko zdolność do wprowadzania zmian w środowisku wykonawczym, ale także zdolność widzenia typu obiektu. Nie wspominając o tym, że większość tak zwanych klas związanych z introspekcją (np. Method, Constructor, Modifier, Field, Member, zasadniczo podobno wszystkie z wyjątkiem Class) znajdują się w java.lang.*reflect* pakiet. Być może pojęcie "odbicie" kompleksowo obejmuje zarówno "introspekcję typu" i modyfikację w czasie wykonywania? - RestInPeace


Odbicie jest zdolnością językową do kontrolowania i dynamicznego wywoływania klas, metod, atrybutów itp. w czasie wykonywania.

Na przykład wszystkie obiekty w Javie mają tę metodę getClass(), który pozwala określić klasę obiektu, nawet jeśli nie znasz go podczas kompilacji (np. jeśli zadeklarowałeś go jako Object) - może się to wydawać banalne, ale taka refleksja nie jest możliwa w mniej dynamicznych językach, takich jak C++. Bardziej zaawansowane zastosowania umożliwiają tworzenie listy i wywoływanie metod, konstruktorów itp.

Refleksja jest ważna, ponieważ pozwala pisać programy, które nie muszą "wiedzieć" wszystkiego podczas kompilacji, co czyni je bardziej dynamicznymi, ponieważ mogą być powiązane w czasie wykonywania. Kod można zapisać na znanych interfejsach, ale rzeczywiste klasy, które mają być używane, można utworzyć za pomocą refleksji z plików konfiguracyjnych.

Wiele współczesnych frameworków z tej właśnie przyczyny szeroko wykorzystuje refleksję. Większość innych współczesnych języków również używa refleksji, a w językach skryptowych (takich jak Python) są one jeszcze bardziej ściśle zintegrowane, ponieważ wydaje się bardziej naturalne w ramach ogólnego modelu programowania tych języków.


195
2017-09-01 08:52





Jednym z moich ulubionych zastosowań refleksji jest poniższa metoda Dump Java. Przyjmuje dowolny obiekt jako parametr i używa interfejsu Java Reflect API wypisującego każdą nazwę pola i wartość.

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



Do czego należy ustawić Callcount? - Tom
Dostałem wyjątek w wątku "AWT-EventQueue-0" java.lang.StackOverflowError po uruchomieniu tego. - Tom
@Tomek callCount powinno być ustawione na zero. Jego wartość służy do określenia liczby kart przed każdym wierszem wyjściowym: za każdym razem, gdy zrzut musi zrzucać "obiekt podrzędny", dane wyjściowe będą drukowane jako zagnieżdżone w obiekcie nadrzędnym. Ta metoda sprawdza się, gdy jest zapakowana w inną. Rozważać printDump(Object obj){ System.out.println(dump(obj, 0)); }. - fny
Zdarzenie java.lang.StackOverflowError może zostać utworzone w przypadku odwołań cyklicznych z powodu niezaznaczonej rekursji: buffer.append (dump (wartość, callCount)) - Arnaud P
Czy możesz zwolnić swój kod do domeny publicznej? - stolsvik


Wykorzystuje odbicie

Odbicie jest powszechnie używane przez programy, które wymagają możliwości sprawdzenia lub zmodyfikowania zachowania środowiska wykonawczego aplikacji działających na wirtualnej maszynie Java. Jest to stosunkowo zaawansowana funkcja i powinna być używana tylko przez programistów, którzy dobrze znają podstawy tego języka. Mając to na uwadze, odbicie jest potężną techniką i może umożliwić aplikacjom wykonywanie operacji, które w innym przypadku byłyby niemożliwe.

Funkcje rozszerzalności

Aplikacja może korzystać z zewnętrznych, zdefiniowanych przez użytkownika klas, tworząc wystąpienia obiektów rozszerzalnościowych, używając w pełni kwalifikowanych nazw. Przeglądarki klas i środowiska Visual Development Przeglądarka klas musi być w stanie wyliczyć członków klas. Wizualne środowiska programistyczne mogą skorzystać na wykorzystaniu informacji o typie dostępnych w odbiciu, aby pomóc programistom w napisaniu poprawnego kodu. Debugery i narzędzia testowe Debugery muszą mieć możliwość sprawdzania prywatnych członków na zajęciach. Wiązki testowe mogą wykorzystywać odbicie do systematycznego wywoływania wykrywalnych zestawów API zdefiniowanych dla klasy, aby zapewnić wysoki poziom pokrycia kodu w zestawie testów.

Wady refleksji

Refleksja jest potężna, ale nie powinna być używana bezkrytycznie. Jeśli możliwe jest wykonanie operacji bez użycia odbicia, lepiej jest jej unikać. Podczas uzyskiwania dostępu do kodu poprzez odbicie należy mieć na uwadze następujące kwestie.

  • Obciążenie wydajności

Ponieważ odbicie obejmuje typy, które są dynamicznie rozdzielane, nie można przeprowadzić pewnych optymalizacji wirtualnych maszyn Java. W związku z tym operacje odbijające mają mniejszą wydajność niż ich nieodblaskowe odpowiedniki i należy ich unikać w sekcjach kodu, które są często wywoływane w aplikacjach wrażliwych na wydajność.

  • Ograniczenia bezpieczeństwa

Odbicie wymaga uprawnienia środowiska wykonawczego, które może nie być dostępne podczas pracy z menedżerem zabezpieczeń. Jest to ważne dla kodu, który musi działać w ograniczonym kontekście bezpieczeństwa, na przykład w aplecie.

  • Narażenie wewnętrzne

Ponieważ odbicie pozwala kodowi wykonywać operacje, które byłyby nielegalne w nieodblaskowym kodzie, takie jak dostęp do prywatnych pól i metod, użycie refleksji może spowodować nieoczekiwane efekty uboczne, które mogą sprawić, że kod będzie dysfunkcjonalny i może zniszczyć przenośność. Odblaskowy kod łamie abstrakcje, a zatem może zmieniać zachowanie wraz z aktualizacjami platformy.

źródło: Odbicie interfejsu API


56
2017-10-17 11:59





Odbicie jest kluczowym mechanizmem pozwalającym aplikacji lub frameworkowi pracować z kodem, który mógł nie zostać jeszcze napisany!

Weźmy na przykład typowy plik web.xml. Będzie to zawierało listę elementów serwletu, które zawierają zagnieżdżone elementy klasy serwletu. Kontener serwletu przetworzy plik web.xml i utworzy nowe wystąpienie każdej klasy serwletu poprzez odbicie.

Innym przykładem może być Java API do XML Parsing (JAXP). Gdzie dostawca analizatora składni XML jest "podłączony" przez dobrze znane właściwości systemu, które są używane do konstruowania nowych instancji poprzez odbicie.

I na koniec najbardziej wszechstronnym przykładem jest Wiosna która używa odbicia, aby stworzyć swoją fasolę i do jej intensywnego używania proxy


30
2017-09-01 09:30





Nie każdy język wspiera refleksję, ale zasady są zwykle takie same w językach, które ją obsługują.

Refleksja to zdolność "refleksji" nad strukturą twojego programu. Lub bardziej konkretny. Aby spojrzeć na obiekty i klasy, które posiadasz i programowo odzyskać informacje o metodach, polach i interfejsach, które implementują. Możesz także spojrzeć na takie adnotacje.

Jest to przydatne w wielu sytuacjach. Wszędzie, gdzie chcesz dynamicznie podłączać klasy do swojego kodu. Partie obiektowych mapperów relacyjnych używają odbicia, aby móc tworzyć instancje obiektów z baz danych, nie wiedząc z góry, jakie obiekty będą używać. Architektura wtyczek to kolejne miejsce, w którym refleks jest przydatny. Możliwość dynamicznego ładowania kodu i określania, czy istnieją typy, które implementują odpowiedni interfejs do użycia jako wtyczki, jest ważna w takich sytuacjach.


27
2017-09-01 08:50



Muszę utworzyć instancję obiektów na podstawie danych obecnych w DB. Wierzę, że o tym mówisz. Przykładowy kod bardzo mi pomógł. Z góry dziękuję. - Atom


Odbicie umożliwia dynamiczne tworzenie nowych obiektów, wywoływanie metod i dynamiczne pobieranie / ustawianie operacji na zmiennych klasowych w czasie wykonywania bez wcześniejszej wiedzy o jego implementacji.

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

W powyższym przykładzie parametr null jest obiektem, na który chcesz wywołać metodę. Jeśli metoda jest statyczna, podajesz wartość null. Jeśli metoda nie jest statyczna, podczas wywoływania musisz podać poprawną instancję MyObject zamiast wartości null.

Odbicie umożliwia także dostęp do prywatnego członka / metod klasy:

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);
  • Do kontroli klas (znanej również jako introspekcja) nie ma potrzeby importowania pakietu refleksyjnego (java.lang.reflect). Dostęp do metadanych klasy można uzyskać przez java.lang.Class.

Odbicie to bardzo potężny interfejs API, ale może spowolnić działanie aplikacji, jeśli jest używana w nadmiarze, ponieważ rozwiązuje wszystkie typy w czasie wykonywania.


26
2017-07-08 16:12



Jest to post, który pomógł mi najbardziej w dziale odpowiedzi, dziękuję ci Nikhil - Enoy


Przykład:
Weźmy na przykład zdalną aplikację, która daje twojej aplikacji obiekt, który uzyskasz za pomocą swoich metod API. Teraz na podstawie tego obiektu może być konieczne wykonanie pewnego rodzaju obliczeń.
Dostawca gwarantuje, że obiekt może składać się z 3 typów i musimy wykonać obliczenia na podstawie rodzaju obiektu.
Możemy więc zaimplementować w 3 klasach, z których każda zawiera inną logikę. Oczywiście informacja o obiekcie jest dostępna w środowisku wykonawczym, więc nie można statycznie kodować w celu wykonania obliczeń, dlatego odbicie służy do tworzenia instancji klasy, która jest wymagana do wykonywania obliczeń na podstawie obiekt otrzymany od dostawcy.


18
2018-06-22 15:35



Potrzebuję czegoś podobnego. Przykład pomógłby mi bardzo, ponieważ jestem nowy w koncepcjach refleksji. - Atom
Jestem zdezorientowany: nie możesz tego użyć instanceof określić typ obiektu w czasie wykonywania? - ndm13


Java Reflection jest dość potężny i może być bardzo przydatny. Java Reflection to umożliwia sprawdzać klasy, interfejsy, pola i metody w czasie wykonywania,bez znajomości nazw klas, metod itp. podczas kompilacji. Możliwe jest również tworzenie instancji nowych obiektów, wywoływanie metod i uzyskiwanie / ustawianie wartości pól za pomocą odbicia.

Szybki przykład Java Reflection pokazujący, jak wygląda użycie refleksji:

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

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

Ten przykład uzyskuje obiekt Class z klasy o nazwie MyObject. Za pomocą obiektu klasy otrzymujemy listę metod z tej klasy, iterujemy metody i drukujemy ich nazwy.

Dokładnie, jak to wszystko działa, wyjaśniono tutaj

Edytować: Po prawie 1 roku redaguję tę odpowiedź, ponieważ podczas czytania o refleksji otrzymałem kilka dodatkowych zastosowań Reflection.

  • Spring używa konfiguracji fasoli, takiej jak:


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

Kiedy kontekst Spring przetwarza ten element <bean>, użyje Class.forName (String) z argumentem "com.example.Foo" do utworzenia instancji tej klasy.

Następnie ponownie użyje odbicia, aby uzyskać odpowiedni ustawnik dla elementu <property> i ustawić jego wartość na określoną wartość.

  • Junit używa Reflection specjalnie do testowania metod prywatnych / chronionych.

W przypadku metod prywatnych

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

Dla pól prywatnych,

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

17
2017-09-08 09:40