Jakiś czas temu napisałem tę odpowiedź na pytanie, w jaki sposób uniknąć pobierania i ustawiania każdej zmiennej zmiennej. W tym czasie miałem tylko trudne do zwerbowania przeczucie, że to zły pomysł, ale OP wyraźnie pytał, jak to zrobić. Szukałem tutaj, dlaczego może to stanowić problem, i znalazłem to pytanie , na które odpowiedzi wydaje się przyjmować, że używanie refleksji, chyba że absolutnie konieczne, jest złą praktyką.
Czy zatem ktoś może werbalizować, dlaczego należy unikać refleksji, szczególnie w przypadku generycznego gettera i setera?
java
reflection
HAEM
źródło
źródło
Map
„sget
orazput
, co jest dość niezgrabne sposób robienia rzeczy.Odpowiedzi:
Wady refleksji w ogóle
Odbicie jest trudniejsze do zrozumienia niż kod liniowy.
Z mojego doświadczenia wynika, że refleksja jest funkcją Java na poziomie „eksperta”. Twierdziłbym, że większość programistów nigdy nie używa aktywnie odbicia (tzn. Zużycie bibliotek korzystających z odbicia się nie liczy). Utrudnia to zrozumienie kodu dla tych programistów.
Kod odbicia jest niedostępny dla analizy statycznej
Załóżmy, że mam
getFoo
klasę pobierającą i chcę zmienić jej nazwę nagetBar
. Jeśli nie użyję refleksji, mogę po prostu przeszukać bazę kodugetFoo
i znajdę każde miejsce, które korzysta z gettera, abym mógł go zaktualizować, a nawet jeśli go przegapię, kompilator narzeka.Ale jeśli miejsce, w którym używa się gettera, jest podobne
callGetter("Foo")
icallGetter
działagetClass().getMethod("get"+name).invoke(this)
, to powyższa metoda go nie znajdzie, a kompilator nie będzie narzekał. Tylko wtedy, gdy kod zostanie faktycznie wykonany, otrzymaszNoSuchMethodException
. Wyobraź sobie ból, który zostanie przełknięty, jeśli ten wyjątek (który jest śledzony) zostanie połknięty,callGetter
ponieważ „jest używany tylko z zakodowanymi ciągami, tak naprawdę nie może się zdarzyć”. (Nikt by tego nie zrobił, ktoś mógłby się spierać? Tyle że OP zrobił dokładnie to samo w swojej odpowiedzi SO. Jeśli nazwa pola zostanie zmieniona, użytkownicy standardowego setera nigdy nie zauważą, z wyjątkiem wyjątkowo niejasnego błędu setera, który po cichu nic nie robi. Użytkownicy gettera mogą, jeśli mają szczęście, zauważyć wyjście konsoli zignorowanego wyjątku).Kod odbicia nie jest sprawdzany przez kompilator
Jest to zasadniczo duży podpunkt powyższego. Chodzi o kod refleksyjny
Object
. Typy są sprawdzane w czasie wykonywania. Błędy są wykrywane podczas testów jednostkowych, ale tylko w przypadku posiadania zasięgu. („To tylko getter, nie muszę go testować.”) Zasadniczo, tracisz przewagę, używając Java nad Pythonem, zyskałeś przede wszystkim.Kod odbicia jest niedostępny do optymalizacji
Może nie w teorii, ale w praktyce nie znajdziesz JVM, która wstawia lub tworzy wbudowaną pamięć podręczną
Method.invoke
. Do takich optymalizacji dostępne są normalne wywołania metod. To sprawia, że są znacznie szybsze.Kod refleksji jest po prostu powolny
Dynamiczne wyszukiwanie metod i sprawdzanie typu niezbędne do kodu odbicia jest wolniejsze niż normalne wywołania metod. Jeśli zamienisz ten tani geter jednowierszowy w bestię odbicia, możesz (nie zmierzyłem tego) patrzeć na kilka rzędów wielkości spowolnienia.
Wadą specyficznego gettera / setera
To po prostu zły pomysł, ponieważ twoja klasa nie ma już enkapsulacji. Każde pole, które ma, jest dostępne. Równie dobrze możesz je wszystkie upublicznić.
źródło
Ponieważ getter / setery tranzytowe są obrzydliwością, która nie daje żadnej wartości. Nie zapewniają żadnej enkapsulacji, ponieważ użytkownicy mogą uzyskać rzeczywiste wartości za pośrednictwem właściwości. Są to YAGNI, ponieważ rzadko, jeśli w ogóle, zmienisz implementację pamięci w terenie.
A ponieważ jest to mniej wydajne. Przynajmniej w języku C # getter / setter wstawi się bezpośrednio do pojedynczej instrukcji CIL. W odpowiedzi zastępujesz to 3 wywołaniami funkcji, które z kolei muszą załadować metadane, aby się nad nimi zastanowić. Zasadniczo używałem 10x jako ogólnej zasady kosztów refleksji, ale kiedy szukałem danych, jedna z pierwszych odpowiedzi zawierała tę odpowiedź, która zbliżyła ją do 1000x.
(Utrudnia to debugowanie kodu i szkodzi użyteczności, ponieważ istnieje więcej sposobów jego niewłaściwego użycia, a także szkodzi konserwacji, ponieważ teraz używasz magicznych ciągów zamiast odpowiednich identyfikatorów ...)
źródło
getX()
isetX()
metody, które można znaleźć poprzez odbicie. Fasola niekoniecznie musi zawierać w sobie swoje wartości, mogą być po prostu DTO. Tak więc w Javie narzędzia pobierające są często niezbędnym bólem. Właściwości C # są znacznie, o wiele ładniejsze pod każdym względem.Oprócz przedstawionych już argumentów.
Nie zapewnia oczekiwanego interfejsu.
Użytkownicy oczekują wywoływania foo.getBar () i foo.setBar (wartość), a nie foo.get („bar”) i foo.set („bar”, wartość)
Aby zapewnić interfejs oczekiwany przez użytkowników, należy napisać osobny moduł pobierający / ustawiający dla każdej właściwości. Gdy już to zrobisz, nie ma sensu używać odbicia.
źródło
Oczywiście nie jest to zły pomysł, to po prostu niż w normalnym świecie Java jest zły pomysł (gdy chcesz tylko gettera lub setera). Na przykład niektóre frameworki (spring mvc) lub biblioteki już go używają (jstl) w ten sposób, niektóre parsery json lub xml go używają, jeśli chcesz użyć DSL, będziesz go używać. To zależy od scenariusza przypadku użycia.
W rzeczywistości nic nie jest dobre ani złe.
źródło