Що таке рефлексія і чому вона корисна?


2123

Що таке рефлексія, і чому вона корисна?

Мене особливо цікавить Java, але я припускаю, що принципи однакові у будь-якій мові.


9
Для мене це спосіб отримання імен класів під час виконання та створення об'єктів цього класу.
Набін

64
тому що це популярне питання, я хотів би зазначити, що рефлексія (Без анотацій) повинна бути самим останнім інструментом, до якого ви звертаєтесь при вирішенні проблеми. Я використовую його і люблю, але це скасовує всі переваги статичного введення Java. Якщо вам це потрібно, виділіть його на якомога меншій площі (Один метод або один клас). Більш прийнятно використовувати його в тестах, ніж виробничий код. З анотаціями це повинно бути добре - Головний момент - не вказувати назви класів чи методів як "Strings", якщо ви, можливо, цього уникнете.
Білл К


4
На додаток до коментаря @ BillK: Рефлексія дуже потужна, я б назвав це магією. З великою силою настає велика відповідальність. Використовуйте його, лише якщо ви знаєте, що робите.
MC Імператор

Ви можете уникнути багатьох підводних каменів, пов'язаних із відображенням, використовуючи колектор @Jailbreak. Він забезпечує прямий безпечний для доступу доступ до приватних полів, методів тощо. Нехай компілятор Java безпечно перевіряє ваш код і нехай Manifold генерує базовий доступ для відображення для вас. Детальніше: manifold.systems/docs.html#type-safe-reflection
Скотту

Відповіді:


1716

Відбиття імені використовується для опису коду, який здатний перевіряти інший код у тій же системі (або самому).

Наприклад, скажіть, що у вас в Java є об’єкт невідомого типу, і ви хочете викликати на ньому метод "doSomething", якщо такий існує. Статична система набору тексту Java не справді розроблена для того, щоб підтримувати це, якщо об'єкт не відповідає відомому інтерфейсу, але, використовуючи відображення, ваш код може переглянути об’єкт і дізнатись, чи є у нього метод під назвою "doSomething", а потім викликати його, якщо ви хочу.

Отже, щоб навести приклад коду цього в Java (уявіть, що йдеться про об'єкт foo):

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

Один дуже поширений випадок використання на Java - це використання з примітками. Наприклад, JUnit 4 використовуватиме відображення для перегляду ваших класів для методів, позначених анотацією @Test, а потім викликатиме їх під час запуску одиничного тесту.

Ось кілька хороших прикладів роздумів, щоб розпочати роботу на веб-сторінці http://docs.oracle.com/javase/tutorial/reflect/index.html

І нарешті, так, поняття майже подібні в інших статично типових мовах, які підтримують рефлексію (наприклад, C #). У динамічно набраних мовах описаний вище випадок використання є менш необхідним (оскільки компілятор дозволить викликати будь-який метод на будь-якому об'єкті, не працює під час виконання, якщо його не існує), але другий випадок пошуку методів, позначених або робота певним чином все ще поширена.

Оновлення з коментаря:

Можливість перевіряти код у системі та бачити типи об’єктів - це не відображення, а скоріше Тип інтроспекції. Рефлексія - це здатність вносити зміни під час виконання, використовуючи самоаналіз. Тут потрібна відмінність, оскільки деякі мови підтримують самоаналіз, але не підтримують рефлексію. Одним із таких прикладів є C ++


32
Ви можете, будь ласка, пояснити, у чому значення цього параметра null у цьому рядку Метод Метод = foo.getClass (). getMethod ("doSomething", null);
Кришна Чайтанья

54
Нуль вказує, що параметри методу foo не передаються. Докладніше див. Docs.oracle.com/javase/6/docs/api/java/lang/reflect/… , java.lang.Object ...).
Метт Шеппард

791
Просто для того, щоб прояснити, оскільки на цьому є багато відгуків. Можливість перевіряти код у системі та бачити типи об’єктів - це не відображення, а скоріше Тип інтроспекції. Рефлексія - це здатність вносити зміни під час виконання, використовуючи самоаналіз. Тут потрібна відмінність, оскільки деякі мови підтримують самоаналіз, але не підтримують рефлексію. Одним із таких прикладів є C ++.
bigtunacan

39
Я люблю рефлексію, але якщо у вас є контроль над кодом, то використання відображення, як зазначено у цій відповіді, є нечесним, а отже, зловживанням. Ви повинні використовувати тип інтроспекції (instanceof) та сильні типи. Якщо є якийсь спосіб, окрім роздумів, щоб щось зробити, ось так слід зробити. Рефлексія викликає серйозні душевні болі, оскільки ви втрачаєте всі переваги використання мови, що має статичний набір. Якщо він вам потрібен, він вам потрібен, але навіть тоді я вважаю б готове рішення, наприклад, Spring або щось, що повністю інкапсулює необхідне відображення - IE: нехай у когось іншого будуть головні болі.
Білл К

6
@bigtunacan Звідки ви взяли цю інформацію? Я бачу термін "відображення", який використовується в офіційній документації Java від Oracle для опису не тільки здатності вносити зміни під час виконання, але й здатності бачити тип об'єкта. Не кажучи вже , що більшість так званих «типу інтроспекції» пов'язаних класів (наприклад: Method, Constructor, Modifier, Field, Member, в основному , по- видимому все , крім Class) знаходяться в межах java.lang.*reflect*пакету. Можливо, поняття "рефлексія" всебічно включає в себе як "тип самоаналізу", так і модифікацію під час виконання?
RestInPeace

246

Рефлексія - це здатність мови перевіряти та динамічно викликати класи, методи, атрибути тощо під час виконання.

Наприклад, всі об'єкти в Java мають метод getClass(), який дозволяє визначати клас об'єкта, навіть якщо ви не знаєте його під час компіляції (наприклад, якщо ви оголосили його як Object) - це може здатися тривіальним, але таке відображення неможливо менш динамічними мовами, такими як C++. Більш розвинене використання дозволяє перелічити та викликати методи, конструктори тощо.

Роздуми важливі, оскільки дозволяють писати програми, які не повинні "знати" все під час компіляції, роблячи їх більш динамічними, оскільки вони можуть бути пов'язані між собою під час виконання. Код може бути записаний на відомих інтерфейсах, але фактичні класи, які слід використовувати, можна створити миттєво, використовуючи відображення з конфігураційних файлів.

З цієї причини багато сучасних рамок широко використовують рефлексію. Більшість інших сучасних мов також використовують рефлексію, а в мовах сценаріїв (наприклад, Python) вони ще більш тісно інтегровані, оскільки це відчуває себе природніше в загальній моделі програмування цих мов.


2
Отже, іншими словами, ви можете створити екземпляр з його кваліфікованого імені, і компілятор не буде скаржитися на це (тому що скажіть, що для імені класу ви використовуєте просто String). Тоді під час виконання, якщо цього класу немає, ви отримуєте виняток. Ви в цьому випадку обійшли компілятор. Ви б дали мені якийсь конкретний випадок використання для цього? Я просто не можу уявити, коли я б його обрав.
Фернандо Габріелі

3
@FernandoGabrieli, хоча це правда, що легко створювати помилки виконання під час відображення, але також цілком можливо використовувати відображення, не ризикуючи винятками з виконання. Як натякнув у моїй відповіді, поширене використання роздумів є для бібліотек або фреймворків, які явно не можуть знати структуру програми під час компіляції, оскільки вони складаються окремо від програми. Будь-яка бібліотека, яка використовує "код за умовами", ймовірно, використовує відображення, але не обов'язково використовує магічні рядки.
Ледман

C++має інформацію про тип виконання. RTTI
Айксан

114

Одним з моїх улюблених застосувань для відображення є нижчий метод скидання Java. Він приймає будь-який об'єкт як параметр і використовує API відбиття Java для друку кожного імені поля та значення.

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
Для чого слід встановити Callcount?
Том

8
Я отримав виняток у потоці "AWT-EventQueue-0" java.lang.StackOverflowError, коли я запускав це.
Том

3
@Tom callCountмає бути встановлений на нуль. Це значення використовується для визначення кількості вкладок, що передують кожному рядку виводу: кожен раз, коли дамп потребує скидання "субекти", вихід буде надрукований як вкладений у батьківському. Цей спосіб виявляється корисним, коли його загортають в інший. Розглянемо printDump(Object obj){ System.out.println(dump(obj, 0)); }.
fny

1
Java.lang.StackOverflowError може бути створений у разі кругових посилань через неконтрольовану рекурсію: buffer.append (dump (значення, callCount))
Arnaud P

4
Чи можете ви спеціально випустити свій код у Public Domain?
stolsvik

84

Використання відбиття

Рефлексія зазвичай використовується програмами, які вимагають здатності перевіряти або змінювати поведінку програм, що працюють у віртуальній машині Java. Це відносно вдосконалена функція, і її слід використовувати лише розробникам, які добре розуміють основи мови. Маючи на увазі цей застереження, рефлексія є потужною технікою і може дозволити додаткам виконувати операції, які інакше були б неможливі.

Особливості розширення

Додаток може використовувати зовнішні, визначені користувачем класи, створюючи екземпляри об’єктів розширюваності з використанням їх повнокваліфікованих імен. Браузери класів та середовища візуальної розробки Класичний браузер повинен мати можливість перерахувати членів класів. Середовища візуальної розробки можуть отримати користь від використання інформації про тип, доступної для відображення, щоб допомогти розробнику в написанні правильного коду. Налагоджувачі та інструменти тестування Налагоджувачі повинні мати можливість оглядати приватних членів у класах. Тестові джгути можуть використовувати відображення для систематичного виклику відкритих наборів API, визначених у класі, для забезпечення високого рівня охоплення коду в тестовому наборі.

Недоліки відображення

Рефлексія є потужною, але не повинна застосовуватися без розбору. Якщо можливо виконати операцію без використання відображення, то бажано уникати її використання. Наступні занепокоєння слід пам’ятати під час доступу до коду через відображення.

  • Продуктивність накладних витрат

Оскільки рефлексія включає типи, які динамічно вирішуються, певні оптимізації віртуальної машини Java не можуть бути виконані. Отже, світловідбиваючі операції мають меншу продуктивність, ніж їх невідбиваючі аналоги, і їх слід уникати в розділах коду, які часто називають у чутливих до продуктивності додатках.

  • Обмеження безпеки

Для відображення потрібен дозвіл на час виконання, який може бути відсутнім під час роботи під управлінням безпеки. Це важливо враховувати для коду, який повинен працювати в обмеженому контексті безпеки, наприклад, в аплеті.

  • Експозиція внутрішніх органів

Оскільки відображення дозволяє коду виконувати операції, які були б незаконні в невідбиваючому коді, такі як доступ до приватних полів і методів, використання відображення може призвести до несподіваних побічних ефектів, що може зробити код нефункціональним і може знищити портативність. Рефлексивний код порушує абстракції і тому може змінити поведінку з оновленнями платформи.

джерело: API відображення


44

Рефлексія - це ключовий механізм, який дозволяє програмі або рамкам працювати з кодом, який, можливо, ще не був написаний!

Візьмемо для прикладу свій типовий файл web.xml. Він буде містити перелік елементів сервлетів, які містять вкладені елементи класу сервлетів. Контейнер сервлетів обробляє файл web.xml та створює новий екземпляр кожного класу сервлетів за допомогою відображення.

Іншим прикладом може бути Java API для XML Parsing (JAXP) . Якщо постачальник XML-аналізатора "підключається" через відомі системні властивості, які використовуються для побудови нових екземплярів за допомогою відображення.

І, нарешті, найбільш всеосяжним прикладом є Весна, яка використовує роздуми для створення своїх бобів, і для сильного використання проксі


36

Не кожна мова підтримує рефлексію, але принципи, як правило, однакові в мовах, які її підтримують.

Рефлексія - це здатність "розмірковувати" про структуру вашої програми. Або конкретніше. Щоб переглянути об'єкти та класи, які у вас є, і програмно отримати інформацію про методи, поля та інтерфейси, які вони реалізують. Ви також можете подивитися на речі, як примітки.

Це корисно у багатьох ситуаціях. Скрізь, де ви хочете, зможете динамічно підключати класи до свого коду. Реляційні картографічні об'єкти Лота використовують відображення, щоб мати можливість створювати об'єкти з баз даних, не знаючи заздалегідь, які об’єкти вони будуть використовувати. Плагінні архітектури - це ще одне місце, де рефлексія корисна. У цих ситуаціях важливо мати можливість динамічно завантажувати код і визначати, чи є там типи, які реалізують правильний інтерфейс для використання як плагін.


Мені потрібно інстанціювати об’єкти на основі даних, присутніх у БД. Я вважаю, що це ви говорите. Зразок коду міг би мені дуже допомогти. Заздалегідь спасибі.
Атом

34

Відображення дозволяє інстанціювати нові об'єкти, викликати методи та операції отримати / встановити змінні класу динамічно під час виконання без попередніх знань про його реалізацію.

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

У наведеному вище прикладі нульовим параметром є об'єкт, до якого потрібно викликати метод. Якщо метод статичний, ви надаєте null. Якщо метод не є статичним, тоді при виклику вам потрібно надати дійсний екземпляр MyObject замість null.

Рефлексія також дозволяє отримати доступ до приватного члена / методів класу:

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);
  • Для огляду класів (також відомих як самоаналіз) вам не потрібно імпортувати пакет відображення ( java.lang.reflect). До метаданих класу можна отримати доступ java.lang.Class.

Reflection - це дуже потужний API, але він може уповільнити додаток, якщо використовувати його в надлишку, оскільки він вирішує всі типи під час виконання.


21

Відбиття Java є досить потужним і може бути дуже корисним. Відбиття Java дозволяє перевіряти класи, інтерфейси, поля та методи під час виконання, не знаючи назв класів, методів тощо під час компіляції. Також можливо інстанціювати нові об'єкти, викликати методи та отримати / встановити значення поля за допомогою відображення.

Швидкий приклад відображення Java, щоб показати вам, як виглядає використання відображення:

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

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

Цей приклад отримує об’єкт Class з класу під назвою MyObject. Використовуючи об’єкт класу, приклад отримує список методів цього класу, ітератує методи та роздруковує їх імена.

Як саме це працює, пояснено тут

Редагувати : Через майже 1 рік я редагую цю відповідь, коли читаючи про роздуми, я отримав ще декілька застосувань Reflection.

  • Spring використовує конфігурацію квасолі, таку як:


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

Коли контекст Spring обробляє цей елемент <bean>, він використовуватиме Class.forName (String) з аргументом "com.example.Foo" для створення цього класу.

Потім знову використовуватиме відображення, щоб отримати відповідний сеттер для елемента <властивість> та встановити його значення на вказане значення.

  • Junit використовує Reflection, особливо для тестування приватних / захищених методів.

Для приватних методів

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

Для приватних полів

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

21

Приклад:

Візьмемо для прикладу віддалену програму, яка надає вашій програмі об'єкт, який ви отримуєте за допомогою своїх методів API. Тепер виходячи з об'єкта, можливо, вам доведеться виконати якесь обчислення.

Постачальник гарантує, що об'єкт може бути трьох типів, і нам потрібно проводити обчислення, виходячи з того, який тип об'єкта.

Таким чином, ми можемо реалізувати в 3 класах, кожен з яких містить іншу логіку. об’єкт, отриманий від провайдера.


Мені потрібно щось подібне .. Приклад дуже допоможе мені, оскільки я новачок у роздумах концепцій ..
Atom

2
Я розгублений: ви не можете використовувати instanceofдля визначення типу об'єкта під час виконання?
ndm13

19

Простий приклад для роздумів. У шаховій грі ви не знаєте, що буде переміщено користувачем під час виконання. відображення можна використовувати для виклику методів, які вже реалізовані під час виконання:

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 - це API, який використовується для вивчення або модифікації поведінки методів, класів, інтерфейсів під час виконання.

  1. Необхідні класи для рефлексії наведені в розділі java.lang.reflect package.
  2. Відображення дає нам інформацію про клас, до якого належить об'єкт, а також про методи цього класу, які можна виконати за допомогою об'єкта.
  3. Через відображення ми можемо викликати методи під час виконання незалежно від специфікатора доступу, який використовується для них.

java.langІ java.lang.reflectпакети забезпечують класи для Java Reflection.

Роздум можна використовувати для отримання інформації про -

  1. КласgetClass() метод використовується , щоб отримати ім'я класу , до якого належить об'єкт.

  2. КонструкториgetConstructors() метод використовується для отримання державних конструкторів класу , до якого належить об'єкт.

  3. МетодиgetMethods() метод використовується для отримання загальних методів класу , до якої належить об'єкти.

Reflection API використовується в основному:

IDE (інтегроване середовище розробки), наприклад, Eclipse, MyEclipse, NetBeans тощо.
Інструменти для налагодження та тестування тощо.

Переваги використання відображення:

Особливості розширення: Додаток може використовувати зовнішні, визначені користувачем класи, створюючи екземпляри об’єктів розширюваності за допомогою їх повнокваліфікованих імен.

Інструменти налагодження та тестування: Налагоджувачі використовують властивість відображення для огляду приватних членів на класах.

Недоліки:

Накладні показники: Отражаючі операції мають меншу продуктивність, ніж їх невідбиваючі аналоги, і їх слід уникати в розділах коду, які часто називають у чутливих до продуктивності додатках.

Експозиція внутрішніх приміщень: рефлексивний код порушує абстракції і, отже, може змінити поведінку при оновленнях платформи.

Ref: Java Reflection javarevisited.blogspot.in


4
Я б додав до недоліків " Це порушує рефакторинг ". Для мене це головна причина, щоб максимально уникати роздумів.
SantiBailors

Отже, це дозволяє нам (наприклад) перевірити класи, які у нас є (чи є у нас їх екземпляри чи ні), правильно? Під цим я маю на увазі отримати свої методи або конструктори і використовувати їх для створення нових примірників / викликати їх. Чому ми говоримо "зміна поведінки програми", якщо поведінка вже є, але з іншим кодом? Чому його називають "рефлексією"? Спасибі
Фернандо Габріелі

15

Згідно з моїм розумінням:

Рефлексія дозволяє програмісту динамічно отримувати доступ до об'єктів програми. тобто, кодуючи додаток, якщо програміст не знає про клас або його методи, він може динамічно використовувати такий клас (під час виконання), використовуючи відображення.

Він часто використовується в сценаріях, коли ім'я класу часто змінюється. Якщо така ситуація виникає, програмісту складно переписувати додаток і змінювати назву класу знову і знову.

Натомість, використовуючи роздуми, потрібно потурбуватися про можливу зміну назви класу.


15

Рефлексія - це набір функцій, який дозволяє отримувати інформацію про час виконання програми та змінювати її поведінку (з деякими обмеженнями).

Це корисно, оскільки дозволяє змінювати поведінку виконання, залежно від метаінформації вашої програми, тобто ви можете перевірити тип повернення функції та змінити спосіб вирішення ситуації.

Наприклад, у C # ви можете завантажити збірку (.dll) під час виконання і вивчити її, переміщаючись по класах та вживаючи дій відповідно до того, що ви знайшли. Він також дозволяє створювати екземпляр класу під час виконання, викликати його метод тощо.

Де воно може бути корисним? Корисний не кожен раз, але для конкретних ситуацій. Наприклад, ви можете використовувати його для отримання імені класу для ведення журналів, динамічного створення обробників подій відповідно до того, що вказано у файлі конфігурації тощо.


11

Я просто хочу додати певного моменту до всього, що було перераховано.

За допомогою API Reflection ви можете написати універсальний toString()метод для будь-якого об'єкта.

Це корисно при налагодженні.

Ось декілька прикладів:

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

Рефлексія полягає в тому, щоб дозволити об'єкту побачити їх зовнішній вигляд. Цей аргумент, здається, не має нічого спільного з роздумом. Насправді це здатність «самоідентифікуватись».

Сам роздум - це слово для таких мов, яким не вистачає можливостей самопізнання та самочуття, як Java та C #. Оскільки вони не мають можливостей самопізнання, коли ми хочемо спостерігати, як це виглядає, ми повинні мати ще одну річ, щоб задуматися про те, як це виглядає. Відмінні динамічні мови, такі як Ruby та Python, можуть сприймати своє відображення без допомоги інших людей. Можна сказати, що об’єкт Java не може сприймати, як він виглядає без дзеркала, який є об’єктом класу відображення, але об’єкт у Python може сприймати його без дзеркала. Тож тому нам потрібне роздуми на Java.


type (), isin substance (), callable (), dir () та getattr (). .... це пітонічні роздуми про дзвінки
AnthonyJClink

9

З сторінки документації Java

java.lang.reflectПакет містить класи та інтерфейси для отримання відображаючої інформації про класи та об'єкти. Рефлексія дозволяє програмному доступу до інформації про поля, методи та конструктори завантажених класів та використання відображених полів, методів та конструкторів для роботи над своїми аналогами в межах обмежень безпеки.

AccessibleObjectдозволяє придушити перевірку доступу, якщо є необхідна ReflectPermission.

Класи в цьому пакеті, а також java.lang.Classвміщують додатки, такі як налагоджувачі, інтерпретатори, інспектори об'єктів, браузери класів та такі послуги, як Object Serializationі, JavaBeansяким потрібен доступ або до публічних членів цільового об'єкта (на основі його класу виконання), або до членів, оголошених заданий клас

Він включає наступні функціональні можливості.

  1. Отримання об'єктів класу,
  2. Вивчення властивостей класу (поля, методи, конструктори),
  3. Встановлення та отримання значень поля,
  4. Методи виклику,
  5. Створення нових екземплярів об'єктів.

Ознайомтесь із цим посиланням на документацію щодо методів, які піддаються Classкласу.

З цієї статті (Деніс Сосноскі, президент, Sosnoski Software Solutions, Inc) та цієї статті (pdf-розвідки щодо безпеки):

Я бачу значні недоліки, ніж використання використання Reflection

Користувач рефлексії:

  1. Це забезпечує дуже універсальний спосіб динамічного зв’язку компонентів програми
  2. Це корисно для створення бібліотек, які працюють з об'єктами дуже загально

Недоліки відображення:

  1. Відображення набагато повільніше, ніж прямий код, коли використовується для доступу до поля та методу.
  2. Це може затьмарити те, що відбувається насправді у вашому коді
  3. Він обходить вихідний код, може створити проблеми з технічним обслуговуванням
  4. Код відбиття також складніше, ніж відповідний прямий код
  5. Це дозволяє порушити основні обмеження безпеки Java, такі як захист доступу до даних та безпека типу

Загальні зловживання:

  1. Завантаження обмежених класів,
  2. Отримання посилань на конструктори, методи або поля обмеженого класу,
  3. Створення нових екземплярів об'єкта, виклик методів, отримання або встановлення значень поля обмеженого класу.

Подивіться на це питання SE щодо зловживання відображенням:

Як я читаю приватне поле на Java?

Підсумок:

Небезпечне використання його функцій, що проводяться в межах системного коду, також може легко призвести до компромісу режиму безпеки Java l. Тому використовуйте цю функцію економно


Способом уникнути проблем продуктивності Reflection в деяких випадках є клас Woozleвивчити інші класи при запуску, щоб побачити, які з них мають статичний RegisterAsWoozleHelper()метод, і викликати всі такі методи, які він знаходить, за допомогою зворотного виклику, який вони можуть використовувати, щоб розповісти Woozleпро себе, уникаючи потрібно використовувати Reflection, одночасно, наприклад, десеріалізуючи дані.
supercat

9

Як випливає з самого назви, воно відображає те, що він містить, наприклад, метод класу тощо, крім надання функції для виклику методу, що створює екземпляр динамічно під час виконання.

Він використовується багатьма рамками та додатками в лісі, щоб викликати послуги, не знаючи код.


7

Рефлексія дає можливість писати більш загальний код. Це дозволяє створити об’єкт під час виконання та викликати його метод під час виконання. Отже, програму можна зробити високопараметризованою. Це також дозволяє самоаналізувати об'єкт і клас для виявлення його змінних та методу, що потрапляє у зовнішній світ.


6

Reflectionмає безліч застосувань . Той, з ким я більш знайомий, - це вміти створювати код на льоту.

IE: динамічні класи, функції, конструктори - на основі будь-яких даних (xml / масив / результати sql / hardcoded / тощо)


1
Ця відповідь буде набагато кращою, якби ви дали лише один незвичайний приклад згенерованого коду або з результату SQL, або з XML-файлу тощо.
ThisClark

Нема проблем. Я використовував відображення у програмі Windows, яка динамічно генерує інтерфейс на основі XML, взятого з бази даних.
Ess Kay

Таким чином, я створив клас, який показує користувачеві звіт. Цей звіт має такі параметри, як Date (from to) id, або що інше. Ця інформація зберігається у форматі xml. Тож спочатку ми маємо вибір звіту. На основі обраного звіту форма отримує xml. Після отримання файлу xml він використовує відображення для створення класу з полями на основі відображених типів. Після того, як ви перейдете на інший звіт, шифер очищається та створюються нові поля на основі xml. Отже, це по суті динамічна форма, заснована на рефлексії. Я також використовував інші способи, але це має бути настійною надією, яка допомагає
Есс Кей

3

Я хочу відповісти на це питання на прикладі. Перш за все, Hibernateпроект використовує Reflection APIдля створення CRUDзаяв, щоб подолати прірву між запущеним додатком і зберіганням. Коли в домені все змінюється, Hibernateнеобхідно знати про них, щоб зберігати їх у сховищі даних і навпаки.

Альтернативно працює Lombok Project. Він просто вводить код під час компіляції, в результаті чого код вставляється у ваші доменні класи. (Я думаю, що це нормально для жителів та сеттерів)

Hibernateвибрано, reflectionоскільки він має мінімальний вплив на процес збирання програми.

І з Java 7 у нас є MethodHandles, яка працює як Reflection API. У проектах для роботи з реєстраторами ми просто копіюємо та вставляємо наступний код:

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

Тому що важко зробити помилку помилки в цьому випадку.


3

Як я вважаю, найкраще пояснити на прикладі, і жодна з відповідей, схоже, не робить цього ...

Практичним прикладом використання роздумів може бути сервер мови Java, написаний на Java, або сервер мови мови PHP, написаний на PHP, і т. Д. Мовний сервер дає ваші здібності IDE, такі як автозаповнення, перехід до визначення, контекстна допомога, підказки типів та інше. Для того, щоб усі імена тегів (слова, які можна автозавершити) відображали всі можливі збіги під час введення мовного сервера, повинні перевірити все про клас, включаючи блоки doc та приватні члени. Для цього йому потрібне відображення зазначеного класу.

Іншим прикладом може бути одиничний тест приватного методу. Один із способів зробити це - створити рефлексію та змінити область застосування методу на публічну на етапі налаштування тесту. Звичайно, можна стверджувати, що приватні методи не слід перевіряти безпосередньо, але це не в цьому.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.