Питання Що таке відображення і чому це корисно?


Що таке відображення, і чому це корисно?

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


1696
2017-09-01 08:39


походження


Для мене це спосіб отримання імен класів під час виконання та створення об'єктів цього класу. - Nabin
тому що це популярне запитання, я хотів би зазначити, що рефлексія (без анотацій) повинна бути самим останнім інструментом, який ви використовуєте при вирішенні проблеми. Я використовую його і люблю це, але це скасовує всі переваги статичного друку Java. Якщо вам це потрібно, виділіть його на маленьку область, наскільки це можливо (один метод або один клас). Більше прийнятно використовувати його в тестах, ніж код виробництва. З анотаціями це повинно бути добре - головне - не вказати назви класів або методів як "рядки", якщо ви можете уникнути цього. - Bill K
Дивись також: softwareengineering.stackexchange.com/questions/123956/... - givanse


Відповіді:


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

Наприклад, скажімо, у вас є об'єкт невідомого типу в 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 ++


1417
2017-09-01 08:44



Чи можете ви, будь ласка, пояснити значення цього нульового параметра в цій лінії Method method = foo.getClass (). getMethod ("doSomething", null); - Krsna Chaitanya
Значення null вказує на те, що параметри не передаються методу foo. Побачити docs.oracle.com/javase/6/docs/api/java/lang/reflect/..., java.lang.Object ...) для деталей. - Matt Sheppard
Просто очистити, оскільки в ньому дуже багато. Можливість перевірки коду в системі і види типів об'єктів - це не рефлексія, а просто Type Self-Inspection. Відображення є тоді здатністю робити модифікації під час виконання, використовуючи самоаналіз. Відмінність тут необхідна, оскільки деякі мови підтримують інтроспекцію, але не підтримують відображення. Одним з таких прикладів є C ++. - bigtunacan
Я люблю рефлексію, але якщо ви контролюєте код, тоді використання відбиття, як зазначено у цій відповіді, є незбалансованим, а отже, зловживанням - слід використовувати Type Introspection (instanceof) і сильні типи. Якщо є якийсь спосіб, а не рефлексія, щоб щось зробити, саме так треба зробити. Відображення викликає серйозні серйозні захворювання, тому що ви втрачаєте всі переваги використання статично набраної мови. Якщо вам це потрібно, то вам це потрібно, однак навіть тоді я б розглянув таке рішення, як Spring або щось, що повністю перекриває необхідне відображення - IE: нехай хтось ще має головний біль. - Bill K
@bigtunacan Де ти отримав цю інформацію? Я бачу термін "відбиття", який використовується в офіційній документації Java від Oracle, щоб описати не тільки можливість внесення змін під час виконання, а й можливість бачити тип об'єкта. Не кажучи вже про те, що більшість так званих "типу самоаналізу" класів (наприклад: Method, Constructor, Modifier, Field, Member, в основному очевидно все крім Class) знаходяться в межах java.lang.*reflect* пакет Може бути, поняття "рефлексія" комплексно включає як "тип самоаналізу", так і модифікацію під час виконання? - RestInPeace


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

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

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

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


195
2017-09-01 08:52





Одним з моїх улюблених способів відображення є нижчий метод дамп 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();
}

84
2017-09-02 16:15



Що слід встановити на Callcount? - Tom
Я отримав Виняток у потоці "AWT-EventQueue-0" java.lang.StackOverflowError, коли я запустив це. - Tom
@Том callCount слід встановити на нуль. Це значення використовується для визначення, скільки вкладок повинно передувати кожному рядку виводу: кожен раз, коли дамп повинен скинути "субоб'єкт", вихід буде друкувати як вкладене в батьківському. Цей метод виявляється корисним, якщо його загорнути в інший. Розглянемо printDump(Object obj){ System.out.println(dump(obj, 0)); }. - fny
Java.lang.StackOverflowError може бути створений у випадку циркулярних посилань через невстановлену рекурсію: buffer.append (dump (value, callCount)) - Arnaud P
Чи можете ви спеціально випустити свій код у загальнодоступний домен, будь ласка, будь ласка? - stolsvik


Використання рефлексії

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

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

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

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

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

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

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

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

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

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

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

джерело: API Reflection


56
2017-10-17 11:59





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

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

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

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


30
2017-09-01 09:30





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

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

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


27
2017-09-01 08:50



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


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

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

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

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

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.

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


26
2017-07-08 16:12



Це повідомлення, яке найбільше допомогло мені у розділі відповідей, дякую вам Ніхіль - Enoy


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


18
2018-06-22 15:35



Мені потрібно щось подібне .. Приклад міг би мені допомогти, як я новачок у рефлексії концепцій .. - Atom
Я заплутався: ви не можете використовувати instanceof визначити тип об'єкта під час виконання? - ndm13