Co to jest refleksja i dlaczego jest przydatna?

2123

Co to jest odbicie i dlaczego jest przydatne?

Szczególnie interesuję się Javą, ale zakładam, że zasady są takie same w każdym języku.

Lehane
źródło
9
Dla mnie jest to sposób na uzyskiwanie nazw klas w czasie wykonywania i tworzenie obiektów tej klasy.
Nabin
64
ponieważ jest to popularne pytanie, chciałbym zwrócić uwagę, że refleksja (bez adnotacji) powinna być ostatnim narzędziem, do którego się wybierasz podczas rozwiązywania problemu. Używam go i uwielbiam, ale niweluje wszystkie zalety statycznego pisania w Javie. Jeśli potrzebujesz, odizoluj go do możliwie najmniejszego obszaru (jedna metoda lub jedna klasa). Bardziej dopuszczalne jest użycie go w testach niż kodu produkcyjnego. Z adnotacjami powinno być w porządku - Głównym celem nie jest określenie nazw klas lub metod jako „Ciągów”, jeśli można tego uniknąć.
Bill K
4
Oprócz komentarza @ BillK: Odbicie jest bardzo silne, nazwałbym to magią. Z dużą mocą przychodzi duża odpowiedzialność. Używaj go tylko wtedy, gdy wiesz, co robisz.
MC Emperor,
Możesz uniknąć wielu pułapek związanych z odbiciem za pomocą Kolektora @Jailbreak. Zapewnia bezpośredni, bezpieczny dla typu dostęp do prywatnych pól, metod itp. Pozwól kompilatorowi Java bezpiecznie zweryfikować kod i pozwól Manifoldowi wygenerować dostęp do odbicia. Dowiedz się więcej: manifold.systems/docs.html#type-safe-reflection
Scott

Odpowiedzi:

1716

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

Załóżmy na przykład, że masz obiekt Java o nieznanym typie i chciałbyś wywołać na nim metodę „doSomething”, jeśli taka istnieje. System statycznego pisania w Javie tak naprawdę nie jest w stanie tego obsługiwać, chyba że obiekt jest zgodny ze znanym interfejsem, ale używając odbicia, twój kod może spojrzeć na obiekt i dowiedzieć się, czy ma metodę o nazwie „doSomething”, a następnie wywołać go, jeśli chcieć.

Tak więc, aby dać ci przykład tego kodu w Javie (wyobraź sobie, że przedmiotowy obiekt to foo):

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

Jednym z bardzo częstych przypadków użycia w Javie jest użycie z adnotacjami. Na przykład JUnit 4 użyje refleksji, aby przejrzeć twoje klasy w poszukiwaniu metod oznaczonych adnotacją @Test, a następnie wywoła je podczas uruchamiania testu jednostkowego.

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

I w końcu tak, pojęcia są bardzo podobne w innych statycznie typowanych językach, które obsługują refleksję (jak C #). W językach o typie dynamicznym opisany powyżej przypadek użycia jest mniej konieczny (ponieważ kompilator pozwoli na wywołanie dowolnej metody na dowolnym obiekcie, aw przypadku uruchomienia nie powiedzie się, jeśli nie istnieje), ale drugi przypadek szukania metod oznaczonych lub praca w określony sposób jest wciąż powszechna.

Aktualizacja z komentarza:

Możliwość sprawdzenia kodu w systemie i zobaczenia typów obiektów nie jest odbiciem, a raczej introspekcją typu. Refleksja to zatem możliwość wprowadzania modyfikacji w czasie wykonywania przy użyciu introspekcji. Rozróżnienie jest tutaj konieczne, ponieważ niektóre języki obsługują introspekcję, ale nie obsługują refleksji. Jednym z takich przykładów jest C ++

Matt Sheppard
źródło
32
czy możesz wyjaśnić, jakie jest znaczenie tego parametru zerowego w tym wierszu Metoda metody = foo.getClass (). getMethod ("doSomething", null);
Krsna Chaitanya
54
Wartość null wskazuje, że do metody foo nie są przekazywane żadne parametry. Szczegóły znajdują się w docs.oracle.com/javase/6/docs/api/java/lang/reflect/… , java.lang.Object ...).
Matt Sheppard
791
Żeby to wyjaśnić, ponieważ ma tak wiele pozytywnych opinii. Możliwość sprawdzenia kodu w systemie i zobaczenia typów obiektów nie jest odbiciem, a raczej introspekcją typu. Refleksja to zatem możliwość wprowadzania modyfikacji w czasie wykonywania przy użyciu introspekcji. Rozróżnienie jest tutaj konieczne, ponieważ niektóre języki obsługują introspekcję, ale nie obsługują refleksji. Jednym z takich przykładów jest C ++.
bigtunacan
39
Uwielbiam refleksję, ale jeśli masz kontrolę nad kodem, to użycie refleksji, jak określono w tej odpowiedzi, jest niestosowne i dlatego stanowi nadużycie - powinieneś użyć introspekcji typu (instanceof) i silnych typów. Jeśli jest coś innego niż refleksja, aby coś zrobić, właśnie tak należy to zrobić. Refleksja powoduje poważny ból serca, ponieważ tracisz wszystkie zalety używania języka o typie statycznym. Jeśli go potrzebujesz, potrzebujesz go, ale nawet wtedy rozważę gotowe rozwiązanie takie jak Spring lub coś, co całkowicie zamyka odbicie - konieczne: IE: pozwól komuś mieć bóle głowy.
Bill K
6
@bigtunacan Skąd masz te informacje? Widzę termin „odbicie” używany w oficjalnej dokumentacji Java firmy Oracle w celu opisania nie tylko możliwości wprowadzania zmian w czasie wykonywania, ale także możliwości zobaczenia typu obiektu. Nie wspominając, że większość tak zwanych „typ introspekcji” Related klas (ex: Method, Constructor, Modifier, Field, Member, w zasadzie wszystko oprócz pozornie Class) mieszczą się w java.lang.*reflect*opakowaniu. Być może pojęcie „refleksja” kompleksowo obejmuje zarówno „introspekcję typu”, jak i modyfikację w czasie wykonywania?
RestInPeace,
246

Refleksja to zdolność języka do sprawdzania i dynamicznego wywoływania klas, metod, atrybutów itp. W czasie wykonywania.

Na przykład wszystkie obiekty w Javie mają metodę getClass(), która pozwala określić klasę obiektu, nawet jeśli nie znasz go w czasie kompilacji (np. Jeśli zadeklarowałeś go jako Object) - może się to wydawać trywialne, ale takie odbicie nie jest możliwe w mniej dynamicznych językach, takich jak C++. Bardziej zaawansowane zastosowania pozwalają wyświetlać i wywoływać metody, konstruktory itp.

Refleksja jest ważna, ponieważ pozwala pisać programy, które nie muszą „wiedzieć” wszystkiego w czasie kompilacji, dzięki czemu są bardziej dynamiczne, ponieważ można je ze sobą łączyć w czasie wykonywania. Kod można napisać w oparciu o znane interfejsy, ale rzeczywiste klasy, które mają być użyte, mogą zostać utworzone za pomocą odbicia od plików konfiguracyjnych.

Wiele współczesnych ram bardzo intensywnie wykorzystuje refleksję z tego właśnie powodu. Większość innych współczesnych języków również używa refleksji, a w językach skryptowych (takich jak Python) są one jeszcze ściślej zintegrowane, ponieważ wydaje się to bardziej naturalne w ogólnym modelu programowania tych języków.

Liedman
źródło
2
Innymi słowy, możesz utworzyć instancję na podstawie jej kwalifikowanej nazwy, a kompilator nie będzie na nią narzekał (ponieważ powiedzmy, że używasz tylko ciągu znaków dla nazwy klasy). Następnie, w czasie wykonywania, jeśli ta klasa nie jest obecna, otrzymasz wyjątek. W tym przypadku ominąłeś kompilator. Czy dałbyś mi konkretny przypadek użycia tego? Po prostu nie mogę sobie wyobrazić, kiedy to wybiorę.
Fernando Gabrieli
3
@FernandoGabrieli, chociaż prawdą jest, że łatwo jest tworzyć błędy środowiska wykonawczego z odbiciem, jest również całkowicie możliwe użycie odbicia bez ryzyka wyjątków środowiska wykonawczego. Jak wskazano w mojej odpowiedzi, powszechnym zastosowaniem refleksji są biblioteki lub frameworki, które wyraźnie nie znają struktury aplikacji w czasie kompilacji, ponieważ są one kompilowane oddzielnie od aplikacji. Każda biblioteka, która używa „kodu zgodnego z konwencją”, prawdopodobnie używa refleksji, ale niekoniecznie używa magicznych ciągów.
Liedman
C++ma informacje o typie wykonawczym. RTTI
Ayxan,
114

Jednym z moich ulubionych zastosowań refleksji jest poniższa metoda zrzutu Java. Bierze dowolny obiekt jako parametr i używa interfejsu API refleksji Java do wydrukowania każdej nazwy i wartości pola.

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();
}
Ben Williams
źródło
8
Na czym należy ustawić Callcount?
Tom
8
Otrzymałem wyjątek w wątku „AWT-EventQueue-0” java.lang.StackOverflowError, gdy go uruchomiłem.
Tom
3
@ Tom callCountpowinien być ustawiony na zero. Jego wartość służy do określenia, ile kart powinno poprzedzać każdy wiersz danych wyjściowych: za każdym razem, gdy zrzut musi zrzucić „podobiekt”, dane wyjściowe byłyby drukowane jako zagnieżdżone w obiekcie nadrzędnym. Ta metoda okazuje się przydatna, gdy jest zapakowana w inną. Zastanów się printDump(Object obj){ System.out.println(dump(obj, 0)); }.
fny
1
Błąd java.lang.StackOverflowError może zostać utworzony w przypadku odwołań cyklicznych z powodu niezaznaczonej rekurencji: buffer.append (dump (wartość, callCount))
Arnaud P
4
Czy możesz konkretnie wydać swój kod w domenie publicznej, proszę?
stolsvik
84

Zastosowania odbicia

Odbicie jest powszechnie stosowane przez programy, które wymagają zdolności do sprawdzania lub modyfikowania działania środowiska wykonawczego aplikacji działających na maszynie wirtualnej Java. Jest to stosunkowo zaawansowana funkcja i powinna być używana tylko przez programistów, którzy dobrze znają podstawy 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 instancje obiektów rozszerzalności przy użyciu ich w pełni kwalifikowanych nazw. Przeglądarki klas i środowiska projektowania wizualnego Przeglądarka klas musi być w stanie wyliczyć członków klas. Środowiska projektowania wizualnego mogą skorzystać z korzystania z informacji o typie dostępnych w odbiciu, aby pomóc deweloperowi w pisaniu poprawnego kodu. Debugery i narzędzia testowe Debugery muszą mieć możliwość badania prywatnych członków w klasach. Wiązki testowe mogą korzystać z refleksji w celu systematycznego wywoływania interfejsów API zestawu możliwych do wykrycia zdefiniowanych w klasie, aby zapewnić wysoki poziom pokrycia kodu w zestawie testów.

Wady odbicia

Odbicie jest potężne, ale nie powinno się go używać bez rozróżnienia. Jeśli możliwe jest wykonanie operacji bez użycia odbicia, lepiej unikać jej używania. Podczas uzyskiwania dostępu do kodu za pośrednictwem refleksji należy pamiętać o następujących kwestiach.

  • Wydajność narzut

Ponieważ odbicie obejmuje typy rozwiązywane dynamicznie, nie można wykonać niektórych optymalizacji maszyny wirtualnej Java. W rezultacie operacje odbijające mają mniejszą wydajność niż ich nieodblaskowe odpowiedniki i należy ich unikać w częściach kodu, które są często wywoływane w aplikacjach wrażliwych na wydajność.

  • Ograniczenia bezpieczeństwa

Odbicie wymaga zezwolenia środowiska wykonawczego, które może nie być obecne podczas działania pod menedżerem bezpieczeństwa. Jest to ważne w przypadku kodu, który musi działać w ograniczonym kontekście bezpieczeństwa, takim jak aplet.

  • Ujawnienie wewnętrznych

Ponieważ odbicie umożliwia kodowi wykonywanie operacji, które byłyby nielegalne w kodzie nieodblaskowym, takich jak dostęp do prywatnych pól i metod, użycie odbicia może spowodować nieoczekiwane skutki uboczne, które mogą sprawić, że kod będzie niefunkcjonalny i może zniszczyć przenośność. Kod odblaskowy łamie abstrakcje i dlatego może zmieniać zachowanie wraz z aktualizacjami platformy.

źródło: Reflection API

Desmond Smith
źródło
44

Refleksja jest kluczowym mechanizmem pozwalającym aplikacji lub frameworkowi na pracę z kodem, który mógł nawet jeszcze nie zostać napisany!

Weźmy na przykład typowy plik web.xml. Będzie zawierać 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 for XML Parsing (JAXP) . Gdzie dostawca parsera XML jest „podłączony” przez dobrze znane właściwości systemu, które są używane do konstruowania nowych instancji poprzez odbicie.

I wreszcie, najbardziej wszechstronnym przykładem jest Wiosna, która wykorzystuje odbicie do tworzenia swoich ziaren i do intensywnego korzystania z serwerów proxy

zestaw narzędzi
źródło
36

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

Refleksja to zdolność „refleksji” nad strukturą twojego programu. Lub bardziej konkretnie. Aby spojrzeć na posiadane obiekty i klasy i programowo uzyskać informacje o metodach, polach i interfejsach, które implementują. Możesz także patrzeć na takie rzeczy jak adnotacje.

Przydaje się w wielu sytuacjach. Wszędzie, gdzie chcesz mieć możliwość dynamicznego podłączania klas do swojego kodu. Wiele relacyjnych maperów obiektowych używa refleksji, aby móc tworzyć instancje obiektów z baz danych, nie wiedząc z góry, z jakich obiektów będą korzystać. Architektury wtyczek to kolejne miejsce, w którym przydatne jest odbicie. W takich sytuacjach ważna jest możliwość dynamicznego ładowania kodu i określania, czy istnieją typy, które implementują odpowiedni interfejs do użycia jako wtyczka.

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

Odbicie pozwala na tworzenie instancji nowych obiektów, wywoływanie metod oraz pobieranie / ustawianie operacji na zmiennych klasowych dynamicznie w czasie wykonywania bez wcześniejszej wiedzy o ich 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órym chcesz wywołać metodę. Jeśli metoda jest statyczna, podajesz null. Jeśli metoda nie jest statyczna, wówczas podczas wywoływania należy podać poprawną instancję MyObject zamiast wartości null.

Refleksja pozwala również na 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 inspekcji klas (zwanych również introspekcją) nie trzeba importować pakietu odbicia ( java.lang.reflect). Dostęp do metadanych klasy można uzyskać poprzez java.lang.Class.

Odbicie jest bardzo potężnym API, ale może spowolnić aplikację, jeśli zostanie użyte w nadmiarze, ponieważ rozwiązuje wszystkie typy w czasie wykonywania.

Nikhil Shekhar
źródło
21

Java Reflection jest dość potężny i może być bardzo przydatny. Java Reflection umożliwia sprawdzanie klas, interfejsów, pól i metod w czasie wykonywania, bez znajomości nazw klas, metod itp. W czasie kompilacji. Możliwe jest także tworzenie instancji nowych obiektów, wywoływanie metod i pobieranie / ustawianie wartości pól za pomocą odbicia.

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

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. Korzystając z obiektu klasy, przykład otrzymuje listę metod w tej klasie, iteruje metody i wypisuje ich nazwy.

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

Edycja : Po prawie 1 roku edytuję tę odpowiedź, ponieważ podczas czytania o refleksji mam jeszcze kilka zastosowań Refleksji.

  • Wiosna 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”, aby utworzyć instancję tej klasy.

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

  • Junit używa Reflection szczególnie do testowania metod prywatnych / chronionych.

W przypadku metod prywatnych

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

W przypadku pól prywatnych

Field field = targetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
VdeX
źródło
21

Przykład:

Weźmy na przykład aplikację zdalną, która nadaje aplikacji obiekt uzyskany za pomocą jej metod API. Teraz na podstawie obiektu może być konieczne wykonanie jakiegoś obliczenia.

Dostawca gwarantuje, że obiekt może być 3 typów i musimy wykonać obliczenia na podstawie tego, jaki typ obiektu.

Możemy więc zaimplementować w 3 klasach, z których każda zawiera inną logikę. Oczywiście informacje o obiekcie są dostępne w środowisku wykonawczym, więc nie można statycznie kodować w celu wykonania obliczeń, dlatego odbicie jest używane do utworzenia obiektu klasy, który jest wymagany do wykonania obliczeń na podstawie obiekt otrzymany od dostawcy.

human.js
źródło
Potrzebuję czegoś podobnego .. Przykład bardzo by mi pomógł, ponieważ jestem nowy w koncepcjach refleksji ..
Atom
2
Jestem zdezorientowany: nie możesz użyć instanceofdo określenia typu obiektu w czasie wykonywania?
ndm13
19

Prosty przykład do refleksji. W grze w szachy nie wiesz, co użytkownik poruszy w czasie wykonywania. refleksji można używać do wywoływania metod, które są już zaimplementowane w czasie wykonywania:

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

}
Isuru Jayakantha
źródło
18

Refleksja to interfejs API służący do sprawdzania lub modyfikowania zachowania metod, klas i interfejsów w czasie wykonywania.

  1. Wymagane klasy do refleksji są podane w java.lang.reflect package.
  2. Refleksja dostarcza nam informacji o klasie, do której należy obiekt, a także o metodach tej klasy, które można wykonać za pomocą obiektu.
  3. Dzięki refleksji możemy wywoływać metody w czasie wykonywania, niezależnie od specyfikatora dostępu używanego z nimi.

java.langI java.lang.reflectpakietach klas Java refleksji.

Refleksji można użyć do uzyskania informacji o -

  1. KlasagetClass() metoda jest stosowana, aby uzyskać nazwę klasy, do której obiekt należy.

  2. KonstruktorzygetConstructors() metoda jest stosowana w celu uzyskania konstruktorów publicznych klasy, do której obiekt należy.

  3. MetodygetMethods() metoda jest stosowana w celu uzyskania publicznych metod klasy, do której obiekty należy.

Odbicie API stosuje się głównie:

IDE (zintegrowane środowisko programistyczne), np. Eclipse, MyEclipse, NetBeans itp.
Narzędzia do debugowania i testowania itp.

Zalety używania Refleksji:

Funkcje rozszerzalności: aplikacja może korzystać z zewnętrznych klas zdefiniowanych przez użytkownika, tworząc instancje obiektów rozszerzalności przy użyciu ich w pełni kwalifikowanych nazw.

Narzędzia do debugowania i testowania: Debugery używają właściwości refleksji do badania prywatnych członków klas.

Wady:

Narzut wydajności: Operacje odbijające mają niższą wydajność niż ich nieodblaskowe odpowiedniki i należy ich unikać w częściach kodu, które są często wywoływane w aplikacjach wrażliwych na wydajność.

Ujawnienie elementów wewnętrznych: kod odblaskowy łamie abstrakcje i dlatego może zmieniać zachowanie w przypadku aktualizacji platformy.

Ref: Java Reflection javarevisited.blogspot.in

roottraveller
źródło
4
Dodam do wad „ przerywa refaktoryzację ”. Dla mnie to główny powód, dla którego należy unikać refleksji w jak największym stopniu.
SantiBailors,
Pozwala nam to (na przykład) na sprawdzenie klas, które mamy (niezależnie od tego, czy mamy ich wystąpienia), prawda? Rozumiem przez to ich metody lub konstruktory i używaj ich do tworzenia nowych instancji / wywoływania ich. Dlaczego mówimy „zmiana zachowania programu”, jeśli zachowanie już istnieje, ale ma inny kod? Dlaczego nazywa się to „refleksją”? Dzięki
Fernando Gabrieli
15

Zgodnie z moim zrozumieniem:

Odbicie pozwala programiście na dynamiczny dostęp do elementów w programie. tzn. podczas kodowania aplikacji, jeśli programista nie wie o klasie lub jej metodach, może dynamicznie korzystać z takiej klasy (w czasie wykonywania) za pomocą odbicia.

Jest często używany w scenariuszach, w których nazwa klasy często się zmienia. Jeśli taka sytuacja się pojawi, programista ma trudności z przepisaniem aplikacji i zmienianiem nazwy klasy raz po raz.

Zamiast tego, używając refleksji, trzeba się martwić o ewentualną zmianę nazwy klasy.

pramod
źródło
15

Refleksja to zestaw funkcji, który umożliwia dostęp do informacji o środowisku wykonawczym programu i modyfikowanie jego zachowania (z pewnymi ograniczeniami).

Jest to przydatne, ponieważ pozwala zmienić zachowanie środowiska wykonawczego w zależności od meta informacji twojego programu, tzn. Możesz sprawdzić typ zwracanej funkcji i zmienić sposób obsługi sytuacji.

Na przykład w języku C # można załadować zestaw (a .dll) w środowisku wykonawczym i zbadać go, poruszając się po klasach i podejmując działania zgodnie z tym, co znalazłeś. Pozwala także utworzyć instancję klasy w środowisku wykonawczym, wywołać jej metodę itp.

Gdzie to może być przydatne? Nie jest przydatny za każdym razem, ale w konkretnych sytuacjach. Na przykład możesz go użyć do uzyskania nazwy klasy do celów logowania, do dynamicznego tworzenia procedur obsługi zdarzeń zgodnie z tym, co podano w pliku konfiguracyjnym i tak dalej ...

Jorge Córdoba
źródło
11

Chcę tylko dodać trochę do wszystkiego, co było na liście.

Z Reflection API możesz napisać uniwersalną toString()metodę dla dowolnego obiektu.

Jest to przydatne podczas debugowania.

Oto przykład:

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;
   }    
}
nazar_art
źródło
11

Odbicie ma pozwolić obiektowi zobaczyć ich wygląd. Ten argument wydaje się nie mieć nic wspólnego z refleksją. W rzeczywistości jest to umiejętność „samoidentyfikacji”.

Samo odbicie jest słowem dla takich języków, które nie mają zdolności do samowiedzy i wyczuwania, takich jak Java i C #. Ponieważ nie mają możliwości samowiedzy, kiedy chcemy obserwować, jak to wygląda, musimy mieć jeszcze jedną rzecz do przemyślenia, jak to wygląda. Doskonałe języki dynamiczne, takie jak Ruby i Python, potrafią postrzegać swoje własne odbicie bez pomocy innych osób. Można powiedzieć, że obiekt Java nie może postrzegać tego, jak wygląda bez lustra, które jest przedmiotem klasy odbicia, ale obiekt w Pythonie może postrzegać go bez lustra. Dlatego potrzebujemy refleksji w Javie.

Marcus Thornton
źródło
type (), isinstance (), callable (), dir () i getattr (). .... są to wezwania do odbicia w
pythonie
9

Ze strony dokumentacji Java

java.lang.reflectPakiet zapewnia klasy i interfejsy do uzyskiwania odblaskowych informacji o klasach i obiektach. Refleksja umożliwia programowy dostęp do informacji o polach, metodach i konstruktorach załadowanych klas, a także wykorzystanie pól, metod i konstruktorów odbitych do działania na ich podstawowych odpowiednikach, z zastrzeżeniem ograniczeń bezpieczeństwa.

AccessibleObjectumożliwia zniesienie kontroli dostępu, jeśli jest ReflectPermissionto konieczne .

Klasy w tym pakiecie, wraz z java.lang.Classaplikacjami, takimi jak debuggery, interpretatory, inspektory obiektów, przeglądarki klas i usługi, takie jak Object Serializationi, JavaBeansktóre potrzebują dostępu do publicznych członków obiektu docelowego (na podstawie jego klasy środowiska wykonawczego) lub członków zadeklarowanych przez dana klasa

Obejmuje następujące funkcje.

  1. Uzyskiwanie obiektów klasy,
  2. Badanie właściwości klasy (pola, metody, konstruktory),
  3. Ustawianie i uzyskiwanie wartości pól,
  4. Przywoływanie metod,
  5. Tworzenie nowych instancji obiektów.

Zajrzyj do tego łącza do dokumentacji metod ujawnionych przez Classklasę.

Z tego artykułu (Dennis Sosnoski, prezes, Sosnoski Software Solutions, Inc) i tego artykułu (eksploracja zabezpieczeń pdf):

Widzę znaczące wady niż zastosowania używania Reflection

Użytkownik refleksji:

  1. Zapewnia bardzo wszechstronny sposób dynamicznego łączenia komponentów programu
  2. Jest to przydatne do tworzenia bibliotek, które działają z obiektami w bardzo ogólny sposób

Wady refleksji:

  1. Odbicie jest znacznie wolniejsze niż kod bezpośredni, gdy jest używane do dostępu do pola i metody.
  2. Może zasłaniać to, co faktycznie dzieje się w twoim kodzie
  3. Pomija kod źródłowy, co może powodować problemy z konserwacją
  4. Kod refleksyjny jest również bardziej złożony niż odpowiadający mu kod bezpośredni
  5. Pozwala na naruszenie kluczowych ograniczeń bezpieczeństwa Java, takich jak ochrona dostępu do danych i bezpieczeństwo typu

Ogólne nadużycia:

  1. Ładowanie klas zastrzeżonych,
  2. Uzyskiwanie odniesień do konstruktorów, metod lub pól ograniczonej klasy,
  3. Tworzenie nowych instancji obiektów, wywoływanie metod, pobieranie lub ustawianie wartości pól ograniczonej klasy.

Spójrz na to pytanie SE dotyczące nadużywania funkcji refleksji:

Jak odczytać prywatne pole w Javie?

Podsumowanie:

Niepewne korzystanie z jego funkcji przeprowadzane z poziomu kodu systemowego może również łatwo doprowadzić do kompromisu w trybie bezpieczeństwa Java l. Dlatego używaj tej funkcji oszczędnie

Ravindra babu
źródło
Sposobem na uniknięcie problemów z wydajnością Reflection w niektórych przypadkach jest Woozlepoproszenie klasy o sprawdzenie innych klas podczas uruchamiania, aby zobaczyć, które z nich mają RegisterAsWoozleHelper()metodę statyczną , i wywołanie wszystkich takich metod za pomocą wywołania zwrotnego, którego mogą użyć, aby powiedzieć Woozleo sobie, unikając trzeba użyć Reflection podczas np. deserializacji danych.
supercat
9

Jak sama nazwa wskazuje, odzwierciedla to, co posiada, na przykład metodę klasową itp. Oprócz zapewnienia funkcji do wywoływania metody tworzenia instancji dynamicznie w czasie wykonywania.

Jest używany przez wiele platform i aplikacji pod drzewem do wywoływania usług bez faktycznej znajomości kodu.

Mohammed Sarfaraz
źródło
7

Refleksja daje możliwość napisania bardziej ogólnego kodu. Umożliwia tworzenie obiektu w czasie wykonywania i wywoływanie jego metody w czasie wykonywania. Dzięki temu program może zostać sparametryzowany. Umożliwia także introspekcję obiektu i klasy w celu wykrycia jego zmiennych i metody wystawionej na świat zewnętrzny.

saumik gupta
źródło
6

Reflectionma wiele zastosowań . Ten, który znam bardziej, to możliwość tworzenia kodu w locie.

IE: klasy dynamiczne, funkcje, konstruktory - oparte na dowolnych danych (xml / array / sql results / hardcoded / etc ..)

Ess Kay
źródło
1
Ta odpowiedź byłaby o wiele lepsza, gdybyś podał tylko jeden niezwykły przykład wygenerowanego kodu z wyniku SQL lub pliku XML itp.
ThisClark
Nie ma problemu. Użyłem odbicia w aplikacji Windows, która dynamicznie generuje interfejs oparty na XML pobranym z bazy danych.
Ess Kay,
Zasadniczo stworzyłem klasę, która pokazuje użytkownikowi raport. Ten raport ma parametry takie jak Data (od do) id lub cokolwiek innego. Te informacje są przechowywane w formacie xml. Najpierw mamy wybór raportu. Na podstawie wybranego raportu formularz pobiera plik XML. Po pobraniu xml używa refleksji, aby utworzyć klasę z polami opartymi na typach odbitych. Po przejściu na inny raport tablica jest czyszczona i generowane są nowe pola na podstawie pliku XML. Jest to więc zasadniczo dynamiczna forma oparta na refleksji. Używałem także w inny sposób, ale powinna to wystarczyć Nadzieja, która pomaga
Ess Kay
3

Chcę odpowiedzieć na to pytanie za pomocą przykładu. Przede wszystkim Hibernateprojekt wykorzystuje Reflection APIdo generowania CRUDinstrukcji w celu połączenia przepaści między uruchomioną aplikacją a magazynem trwałości. Kiedy coś się zmienia w domenie, Hibernatemusi wiedzieć o nich, aby zachować je w magazynie danych i odwrotnie.

Alternatywnie działa Lombok Project. Po prostu wstrzykuje kod w czasie kompilacji, co powoduje wstawianie kodu do klas domeny. (Myślę, że to jest w porządku dla pobierających i ustawiających)

Hibernatewybrał, reflectionponieważ ma minimalny wpływ na proces kompilacji aplikacji.

A od Java 7 mamy MethodHandles, który działa jako Reflection API. W projektach do pracy z rejestratorami wystarczy skopiować i wkleić następny kod:

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

Ponieważ w tym przypadku trudno jest popełnić błąd literowy.

BSeitkazin
źródło
3

Uważam, że najlepiej jest wyjaśnić na przykładzie, a żadna z odpowiedzi nie wydaje się, aby to zrobić ...

Praktycznym przykładem użycia odbić może być Java Language Server napisany w Javie lub PHP Language Server napisany w PHP itp. Language Server daje możliwości IDE, takie jak autouzupełnianie, przejście do definicji, pomoc kontekstowa, typy podpowiedzi i wiele innych. Aby mieć wszystkie nazwy znaczników (słowa, które można uzupełniać automatycznie), aby wyświetlały wszystkie możliwe dopasowania podczas pisania, serwer językowy musi sprawdzać wszystko o klasie, w tym bloki dokumentów i członków prywatnych. W tym celu potrzebuje odzwierciedlenia wspomnianej klasy.

Innym przykładem może być test jednostkowy metody prywatnej. Jednym ze sposobów jest utworzenie refleksji i zmiana zakresu metody na publiczną w fazie konfiguracji testu. Oczywiście można argumentować, że prywatne metody nie powinny być testowane bezpośrednio, ale nie o to chodzi.

cprn
źródło