Что такое отражение и почему оно полезно?
Меня особенно интересует Java, но я предполагаю, что принципы одинаковы на любом языке.
Что такое отражение и почему оно полезно?
Меня особенно интересует Java, но я предполагаю, что принципы одинаковы на любом языке.
Отражение названия используется для описания кода, который способен проверять другой код в той же системе (или сам).
Например, предположим, что у вас есть объект неизвестного типа в 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.
И, наконец, да, понятия довольно похожи на другие языки статического типа, которые поддерживают отражение (например, С#). В динамически типизированных языках описанный выше вариант использования менее необходим (поскольку компилятор разрешает вызов любого метода на любой объект, если он не выполняется во время выполнения, если он не существует), но второй случай поиска методов, которые отмечены или работа в определенном смысле все еще распространена.
Обновление от комментария:
Способность проверять код в системе и видеть типы объектов не является отражением, а скорее представляет собой Introspection. Отражение - это способность вносить изменения во время выполнения, используя интроспекцию. Различие необходимо здесь, поскольку некоторые языки поддерживают самоанализ, но не поддерживают рефлексию. Одним из таких примеров является C++
Отражение - это способность языка проверять и динамически вызывать классы, методы, атрибуты и т.д. во время выполнения.
Например, все объекты в Java имеют метод getClass()
, который позволяет определить класс объекта, даже если вы не знаете его во время компиляции (например, если вы объявили его как Object
) - это может кажутся тривиальными, но такое отражение невозможно в менее динамических языках, таких как C++
. Более расширенное использование позволяет вам перечислить и вызвать методы, конструкторы и т.д.
Отражение важно, поскольку оно позволяет вам писать программы, которые не должны "знать" все во время компиляции, делая их более динамичными, так как они могут быть связаны во время выполнения. Код может быть написан против известных интерфейсов, но фактические классы, которые будут использоваться, могут быть созданы с использованием отражения от файлов конфигурации.
Множество современных фреймворков широко используют рефлексию именно по этой причине. Большинство других современных языков также используют отражение, а в языках сценариев (например, Python) они еще более тесно интегрированы, поскольку они более естественны в общей модели программирования этих языков.
Одно из моих любимых отражений - метод дампа ниже. Он принимает любой объект в качестве параметра и использует 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();
}
Использование отражений
Отражение обычно используется программами, которые требуют возможности исследовать или изменять поведение среды выполнения приложений, запущенных на виртуальной машине Java. Это относительно продвинутая функция и должна использоваться только разработчиками, которые хорошо понимают основы языка. Учитывая это предостережение, рефлексия является мощным методом и может позволить приложениям выполнять операции, которые в противном случае были бы невозможны.
Возможности расширения
Приложение может использовать внешние пользовательские классы, создавая экземпляры объектов расширяемости, используя их полностью квалифицированные имена. Браузеры классов и среды визуального развития Браузер классов должен иметь возможность перечислять члены классов. Визуальные среды разработки могут извлечь выгоду из использования информации типа, доступной для размышления, чтобы помочь разработчику в написании правильного кода. Отладчики и средства тестирования Отладчики должны иметь возможность проверять частных членов в классах. Тестовые жгуты могут использовать рефлексию для систематического вызова установленных API-интерфейсов обнаружения, определенных в классе, для обеспечения высокого уровня охвата кода в наборе тестов.
Недостатки отражения
Отражение мощно, но не должно использоваться без разбора. Если можно выполнить операцию без использования отражения, то предпочтительно избегать ее использования. Следующие проблемы следует учитывать при доступе к коду через отражение.
Поскольку отражение включает типы, которые динамически разрешены, некоторые оптимизации виртуальной машины Java не могут быть выполнены. Следовательно, рефлексивные операции имеют более низкую производительность, чем их неотражающие аналоги, и их следует избегать в разделах кода, которые часто используются в приложениях с высокой чувствительностью к характеристикам.
Для отражения требуется разрешение во время выполнения, которое может отсутствовать при работе под управлением безопасности. Это важно для кода, который должен выполняться в ограниченном контексте безопасности, например, в Applet.
Поскольку отражение позволяет коду выполнять операции, которые были бы незаконными в неотражающем коде, например, при доступе к частным полям и методам, использование рефлексии может привести к неожиданным побочным эффектам, что может привести к дисфункции кода и может привести к повреждению переносимости. Отражающий код разбивает абстракции и поэтому может изменять поведение при обновлении платформы.
источник: API Reflection
Отражение - ключевой механизм, позволяющий приложению или фреймворку работать с кодом, который, возможно, еще не был написан!
Возьмем, к примеру, ваш типичный файл web.xml. Это будет содержать список элементов сервлета, которые содержат вложенные элементы класса сервлета. Контейнер сервлета обрабатывает файл web.xml и создает новый экземпляр каждого класса сервлета посредством отражения.
Другим примером может служить Java API для анализа XML (JAXP). Где поставщик XML-парсеров "подключен" через известные системные свойства, которые используются для создания новых экземпляров посредством отражения.
И наконец, наиболее полным примером является Spring, который использует отражение для создания своего beans и для его интенсивного использования прокси
Не каждый язык поддерживает отражение, но принципы обычно одинаковы на языках, которые его поддерживают.
Отражение - это способность "отражать" структуру вашей программы. Или более конкретный. Чтобы просмотреть объекты и классы, которые у вас есть, и программно получить информацию о методах, полях и интерфейсах, которые они реализуют. Вы также можете посмотреть на такие вещи, как аннотации.
Это полезно во многих ситуациях. Всюду вы хотите динамически подключать классы к вашему коду. Множество объектных реляционных карт используют отражение, чтобы иметь возможность создавать объекты из баз данных, не зная заранее, какие объекты они будут использовать. Плагины - это еще одно место, где полезно использовать отражение. Возможность динамически загружать код и определять, существуют ли там типы, которые реализуют правильный интерфейс для использования в качестве плагина, важны в этих ситуациях.
Отражение позволяет создавать новые объекты, вызывать методы и получать/устанавливать операции над переменными класса динамически во время выполнения без предварительного знания его реализации.
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 вместо null.
Reflection также позволяет вам получить доступ к частному члену/методам класса:
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, но он может замедлить работу приложения, если он используется в избытке, поскольку он разрешает все типы во время выполнения.
Пример:
Возьмем, к примеру, удаленное приложение, которое дает вашему приложению объект, который вы получаете с помощью их методов API. Теперь, основываясь на объекте, вам может потребоваться выполнить какое-то вычисление.
Поставщик гарантирует, что объект может быть трех типов, и нам нужно выполнять вычисления в зависимости от типа объекта.
Таким образом, мы можем реализовать в 3 классах, каждый из которых содержит различную логику. Очевидно, что информация об объекте доступна во время выполнения, поэтому вы не можете статически кодировать для выполнения вычислений, поэтому отражение используется для создания экземпляра объекта класса, который требуется для выполнения вычислений на основе объект получен от провайдера.
Java Reflection довольно мощный и может быть очень полезным. Java Reflection позволяет проверять классы, интерфейсы, поля и методы во время выполнения,, не зная имена классов, методов и т.д. Во время компиляции. Также возможно создавать новые объекты, вызывать методы и получать/устанавливать значения полей с помощью отражения.
Быстрый пример Java Reflection, чтобы показать вам, что использует отражение:
Method[] methods = MyObject.class.getMethods();
for(Method method : methods){
System.out.println("method = " + method.getName());
}
В этом примере получен объект класса из класса MyObject. Используя объект класса, пример получает список методов в этом классе, выполняет итерации методов и распечатывает их имена.
Именно здесь объясняется, как все это работает
Изменить. Почти через год я редактирую этот ответ, когда читаю об отражении. Я получил еще несколько применений Reflection.
<bean id="someID" class="com.example.Foo">
<property name="someField" value="someValue" />
</bean>
Когда контекст Spring обрабатывает этот <bean> , он будет использовать Class.forName(String) с аргументом "com.example.Foo" для создания экземпляра этого класса.
Затем он снова использует отражение, чтобы получить соответствующий сеттер для <property> и установите его значение в указанное значение.
Для частных методов
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);
Простой пример для размышления. В шахматной игре вы не знаете, что будет перемещено пользователем во время выполнения. отражение может использоваться для вызова методов, которые уже реализованы во время выполнения:
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);
}
}
Отражение - это API, который используется для изучения или изменения поведения методов, классов, интерфейсов во время выполнения.
java.lang.reflect package
.Пакеты java.lang
и java.lang.reflect
предоставляют классы для отражения java.
Отражение можно использовать для получения информации о -
Класс Метод getClass()
используется для получения имени класса, к которому принадлежит объект.
Конструкторы Метод getConstructors()
используется для получения общих конструкторов класса, к которому принадлежит объект.
Методы Метод getMethods()
используется для получения общедоступных методов класса, к которому принадлежат объекты.
Reflection API используется в основном:
IDE (интегрированная среда разработки), например. Eclipse, MyEclipse, NetBeans и т.д.
Отладчик и тестовые инструменты и т.д.
Преимущества использования Reflection:
Возможности расширяемости: приложение может использовать внешние, определенные пользователем классы, создавая экземпляры объектов расширяемости, используя их полностью квалифицированные имена.
Средства отладки и тестирования: отладчики используют свойство отражения для изучения частных членов классов.
Недостатки:
Накладные расходы производительности: рефлексивные операции имеют более низкую производительность, чем их неотражающие аналоги, и их следует избегать в разделах кода, которые часто вызываются в приложениях с высокой чувствительностью.
Экспонирование внутренних элементов: отражающий код разбивает абстракции и, следовательно, может изменять поведение при обновлении платформы.
Ссылка: Отражение Java javarevisited.blogspot.in p >
В соответствии с моим пониманием:
Отражение позволяет программисту динамически обращаться к объектам в программе. то есть при кодировании приложения, если программист не знает о классе или его методах, он может использовать этот класс динамически (во время выполнения) с помощью отражения.
Он часто используется в сценариях, где имя класса часто меняется. Если такая ситуация возникает, то программисту сложно перезаписать приложение и изменить имя класса снова и снова.
Вместо этого, используя рефлексию, нужно беспокоиться о возможно изменяющемся имени класса.
Reflection - это набор функций, который позволяет вам получать доступ к информации о времени выполнения вашей программы и изменять ее поведение (с некоторыми ограничениями).
Это полезно, потому что это позволяет вам изменять поведение во время выполнения в зависимости от метаинформации вашей программы, то есть вы можете проверить тип возврата функции и изменить способ обработки ситуации.
В С#, например, вы можете загружать сборку (DLL) во время выполнения, проверять ее, перемещаться по классам и принимать действия в соответствии с тем, что вы нашли. Он также позволяет создавать экземпляр класса во время выполнения, вызывать его метод и т.д.
Где это может быть полезно? Не полезно каждый раз, но для конкретных ситуаций. Например, вы можете использовать его для получения имени класса для целей loggin, чтобы динамически создавать обработчики для событий в соответствии с тем, что указано в файле конфигурации и т.д.
Отражение - позволить объекту увидеть их внешний вид. Этот аргумент не имеет никакого отношения к размышлению. Фактически, это способность "идентифицировать себя".
Сама рефлексия - это слово для таких языков, которые не обладают способностью к самопознанию и самочувствию как Java и С#. Поскольку у них нет возможности самопознания, когда мы хотим наблюдать, как это выглядит, у нас должно быть другое дело, чтобы подумать о том, как это выглядит. Отличные динамические языки, такие как Ruby и Python, могут воспринимать свое отражение без помощи других людей. Можно сказать, что объект Java не может воспринимать, как это выглядит без зеркала, которое является объектом класса отражения, но объект в Python может воспринимать его без зеркала. Поэтому нам нужно размышлять на Java.
Я просто хочу добавить точку ко всему, что было указано.
С Reflection API вы можете написать универсальный метод 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 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;
}
}
На странице документации java
Пакет
java.lang.reflect
предоставляет классы и интерфейсы для получения отражающей информации о классах и объектах. Reflection позволяет программный доступ к информации о полях, методах и конструкторах загруженных классов и использование отраженных полей, методов и конструкторов для работы с их базовыми аналогами в рамках ограничений безопасности.
AccessibleObject
позволяет подавлять проверки доступа, если имеется необходимый ReflectPermission
.
Классы в этом пакете наряду с java.lang.Class
включают приложения, такие как отладчики, интерпретаторы, инспекторы объектов, браузеры классов и сервисы, такие как Object Serialization
и JavaBeans
которым нужен доступ либо к публичным членам целевого объекта (на основе его runtime class) или членов, объявленных данным классом
Он включает следующие функции.
Посмотрите ссылку на эту документацию для методов, открытых Class
класса.
Из этой статьи (Деннис Сосноски, президент Sosnoski Software Solutions, Inc) и этой статьи (security-explorations pdf):
Я вижу значительные недостатки, чем использование использования Reflection
Пользователь отражения:
Недостатки отражения:
Общие злоупотребления:
Взгляните на этот вопрос SE относительно злоупотребления функцией отражения:
Как прочитать приватное поле в Java?
Резюме:
Небезопасное использование его функций, выполняемых из системного кода, также может легко привести к компрометации модели безопасности Java. Поэтому используйте эту функцию экономно
Как само название предполагает, что оно отражает то, что оно хранит, например, метод класса и т.д., помимо предоставления функции для вызова метода, создающего экземпляр динамически во время выполнения.
Он используется многими фреймворками и приложением под деревом для вызова сервисов, фактически не зная кода.
Отражение дает вам возможность писать более общий код. Он позволяет создавать объект во время выполнения и вызывать его метод во время выполнения. Следовательно, программа может быть сильно параметризована. Он также позволяет исследовать объект и класс для обнаружения его переменных и метода, открытых во внешнем мире.
Reflection
имеет много использует. Я больше знаком с тем, чтобы иметь возможность создавать код "на лету".
IE: динамические классы, функции, конструкторы - на основе любых данных (xml/array/sql results/hardcoded/etc..)
Я хочу ответить на этот вопрос на примере. Прежде всего, проект Hibernate
использует Reflection API
для генерации CRUD
заявлений для преодоления пропасти между запущенным приложением и хранилищем персистентности. Когда что-то меняется в домене, Hibernate
должен знать о них, чтобы они сохраняли их в хранилище данных и наоборот.
В качестве альтернативы работает Lombok Project
. Он просто вводит код во время компиляции, приводит к введению кода в ваши классы домена. (Я думаю, что это нормально для геттеров и сеттеров)
Hibernate
выбрал reflection
поскольку оно оказывает минимальное влияние на процесс сборки приложения.
А из Java 7 мы имеем MethodHandles
, который работает как Reflection API
. В проектах, чтобы работать с регистраторами, мы просто копируем-вставляем следующий код:
Logger LOGGER = Logger.getLogger(MethodHandles.lookup().lookupClass().getName());
Потому что в этом случае сложно сделать typo-error.
Как мне кажется, лучше объяснить на примере, и ни один из ответов, похоже, не делает этого...
Практическим примером использования отражений может быть языковой сервер Java, написанный на Java, или языковой сервер PHP, написанный на PHP, и т.д. Language Server предоставляет вашим IDE такие возможности, как автозаполнение, переход к определению, контекстная помощь, типы подсказок и многое другое. Чтобы все имена тегов (слова, которые могут быть автоматически заполнены) отображать все возможные совпадения при вводе, языковой сервер должен проверять все, что касается класса, включая блоки документов и закрытые члены. Для этого нужно отражение указанного класса.
Другим примером будет юнит-тест частного метода. Один из способов сделать это - создать отражение и изменить область метода на общедоступную на этапе настройки теста. Конечно, можно утверждать, что частные методы не следует тестировать напрямую, но это не главное.
Java Reflection позволяет проверять классы, интерфейсы, поля и методы во время выполнения, не зная имена классов, методов и т.д. во время компиляции. В основном на уровне каркаса могут быть достигнуты максимальные выгоды от отражения. Байт-код, который компилируется, если для проверки требуется дополнительная модификация во время выполнения, модификация, добавление большего байтового кода в себя или другая программа или другая структура на уровне метода, уровень переменных экземпляра, уровень конструктора, отражение уровня аннотации.
Предположим, что у вас есть метод add(Int a,int b)
. Эквивалентный байтовый код предположим B1
. Если вы предположили, что в вашей системе 1000 методов с именем add
. Теперь вы хотите проверить значение параметра a
и b
до вызова метода add
. Таким образом, вы можете приклеить свой код к другой программе или фреймворку, который использует отражение для динамического изучения значения байтового кода с помощью Object.getClass.getMethod()
. Существует несколько классов для изучения. Он может добавить дополнительную операцию до вызова метода add
. Но сама программа или другая программа или структура не знают об объекте, который имеет метод с именем add
. В основном в зависимости от употребления в основном используется использование рефлексии с использованием аспектного ориентирования.