Что такое отражение и почему оно полезно?
Меня особенно интересует 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
И, наконец, да, концепции очень похожи в других статически типизированных языках, которые поддерживают отражение (например, C #). В языках с динамической типизацией описанный выше вариант использования менее необходим (поскольку компилятор разрешит вызов любого метода для любого объекта, что приведет к сбою во время выполнения, если он не существует), но второй случай поиска методов, которые отмечены или работа определенным образом все еще распространена.
Обновление из комментария:
Способность проверять код в системе и видеть типы объектов - это не отражение, а, скорее, самоанализ типа. Отражение - это возможность вносить изменения во время выполнения, используя интроспекцию. Различие здесь необходимо, поскольку некоторые языки поддерживают самоанализ, но не поддерживают рефлексию. Одним из таких примеров является C ++.
Method, Constructor, Modifier, Field, Member, в основном все, кроме Class) находятся в пакете java.lang.*reflect*. Возможно, отражение концепции всесторонне включает как самоанализ типов, так и модификацию во время выполнения?
- person RestInPeace; 29.04.2016
ExpandoObject
- person Lucas; 24.03.2017
Отражение - это способность языка проверять и динамически вызывать классы, методы, атрибуты и т. д. во время выполнения.
Например, все объекты в Java имеют метод getClass(), который позволяет вам определять класс объекта, даже если вы не знаете его во время компиляции (например, если вы объявили его как Object) - это может показаться тривиальным, но такое отражение невозможно в менее динамичных языках, таких как C++. Более продвинутое использование позволяет вам перечислять и вызывать методы, конструкторы и т. Д.
Отражение важно, поскольку оно позволяет вам писать программы, которым не нужно «знать» все во время компиляции, что делает их более динамичными, поскольку они могут быть связаны вместе во время выполнения. Код может быть написан для известных интерфейсов, но фактические классы, которые будут использоваться, могут быть созданы с использованием отражения из файлов конфигурации.
Именно по этой причине многие современные фреймворки широко используют отражение. Большинство других современных языков также используют отражение, а в языках сценариев (таких как Python) они еще более тесно интегрированы, поскольку это кажется более естественным в рамках общей модели программирования этих языков.
Одно из моих любимых применений отражения - это приведенный ниже метод дампа 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();
}
callCount следует установить в ноль. Это значение используется, чтобы определить, сколько вкладок должно предшествовать каждой строке вывода: каждый раз, когда дампу необходимо выгрузить подобъект, вывод будет печататься как вложенный в родительский объект. Этот метод оказывается полезным, когда его оборачивают в другой. Рассмотрим printDump(Object obj){ System.out.println(dump(obj, 0)); }.
- person fny; 23.11.2012
Использование отражения
Отражение обычно используется программами, которым требуется возможность исследовать или изменять поведение приложений, работающих на виртуальной машине Java, во время выполнения. Это относительно продвинутая функция, и ее следует использовать только разработчикам, которые хорошо разбираются в основах языка. С учетом этого предостережения рефлексия является мощным методом, позволяющим приложениям выполнять операции, которые в противном случае были бы невозможны.
Возможности расширения
Приложение может использовать внешние, определяемые пользователем классы, создавая экземпляры объектов расширяемости, используя их полностью определенные имена. Обозреватели классов и среды визуальной разработки Обозреватель классов должен иметь возможность перечислять члены классов. Среды визуальной разработки могут выиграть от использования информации о типах, доступной в отражении, чтобы помочь разработчику в написании правильного кода. Отладчики и средства тестирования Отладчики должны иметь возможность проверять закрытые члены в классах. Жгуты тестирования могут использовать отражение для систематического вызова API-интерфейсов обнаруживаемого набора, определенных в классе, чтобы гарантировать высокий уровень покрытия кода в наборе тестов.
Недостатки рефлексии
Отражение - это мощное средство, но его нельзя использовать без разбора. Если возможно выполнить операцию без использования отражения, то предпочтительно избегать его использования. При доступе к коду через отражение следует учитывать следующие моменты.
Поскольку отражение включает типы, которые разрешаются динамически, некоторые оптимизации виртуальной машины Java не могут быть выполнены. Следовательно, отражающие операции имеют более низкую производительность, чем их неотражающие аналоги, и их следует избегать в разделах кода, которые часто вызываются в приложениях, чувствительных к производительности.
Для отражения требуется разрешение времени выполнения, которое может отсутствовать при работе под управлением диспетчера безопасности. Это важное соображение для кода, который должен работать в ограниченном контексте безопасности, например, в апплете.
Поскольку отражение позволяет коду выполнять операции, которые были бы незаконными в неотражающем коде, такие как доступ к закрытым полям и методам, использование отражения может привести к неожиданным побочным эффектам, которые могут привести к нарушению функциональности кода и нарушить переносимость. Отражающий код нарушает абстракции и, следовательно, может изменить поведение при обновлении платформы.
источник: API отражения
Отражение - это ключевой механизм, позволяющий приложению или фреймворку работать с кодом, который, возможно, еще даже не был написан!
Возьмем, к примеру, ваш типичный файл web.xml. Он будет содержать список элементов сервлета, которые содержат вложенные элементы класса сервлета. Контейнер сервлета обработает файл web.xml и создаст новый экземпляр каждого класса сервлета посредством отражения.
Другой пример - Java API для анализа XML (JAXP). Если поставщик анализатора XML «подключается» через хорошо известные системные свойства, которые используются для создания новых экземпляров посредством отражения.
И, наконец, наиболее полным примером является Spring, который использует отражение для создания своих bean-компонентов, а также интенсивное использование прокси-серверов.
Не каждый язык поддерживает рефлексию, но принципы обычно одинаковы для языков, которые ее поддерживают.
Рефлексия - это способность размышлять о структуре вашей программы. Или более конкретно. Для просмотра имеющихся у вас объектов и классов и программного получения информации о методах, полях и интерфейсах, которые они реализуют. Вы также можете просматривать такие вещи, как аннотации.
Это полезно во многих ситуациях. Везде, где вы хотите иметь возможность динамически вставлять классы в свой код. Многие объектно-реляционные преобразователи используют отражение, чтобы иметь возможность создавать экземпляры объектов из баз данных, не зная заранее, какие объекты они собираются использовать. Архитектура подключаемых модулей - еще одно место, где рефлексия полезна. В таких ситуациях важна возможность динамически загружать код и определять, есть ли там типы, реализующие правильный интерфейс для использования в качестве подключаемого модуля.
Отражение позволяет создавать экземпляры новых объектов, вызывать методы и операции получения / установки для переменных класса динамически во время выполнения без предварительного знания о его реализации.
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.Отражение - очень мощный API, но при чрезмерном использовании он может замедлить работу приложения, поскольку разрешает все типы во время выполнения.
Java Reflection довольно мощный и может быть очень полезным. Java Reflection позволяет проверять классы, интерфейсы, поля и методы во время выполнения, не зная имен классов, методов и т. Д. Во время компиляции. Также можно создавать экземпляры новых объектов, вызывать методы и получать / устанавливать значения полей с помощью отражения.
Быстрый пример Java Reflection, показывающий, как выглядит использование отражения:
Method[] methods = MyObject.class.getMethods();
for(Method method : methods){
System.out.println("method = " + method.getName());
}
В этом примере объект Class получается из класса MyObject. Используя объект класса, пример получает список методов в этом классе, выполняет итерацию методов и распечатывает их имена.
Здесь подробно объясняется, как все это работает
Изменить: почти через год я редактирую этот ответ, так как, читая об отражении, я получил еще несколько вариантов использования отражения.
<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);
Пример:
Возьмем, к примеру, удаленное приложение, которое предоставляет вашему приложению объект, который вы получаете с помощью их методов API. Теперь на основе объекта вам может потребоваться выполнить какое-то вычисление.
Провайдер гарантирует, что объект может быть 3-х типов, и нам нужно выполнять вычисления на основе того, какой тип объекта.
Таким образом, мы могли бы реализовать 3 класса, каждый из которых содержит различную логику. Очевидно, что информация об объекте доступна во время выполнения, поэтому вы не можете статически кодировать для выполнения вычислений, поэтому отражение используется для создания экземпляра объекта класса, который вам нужен для выполнения вычислений на основе объект получен от провайдера.
instanceof для определения типа объекта во время выполнения?
- person ndm13; 11.01.2018
Простой пример для размышления. В шахматной игре вы не знаете, что будет перемещать пользователь во время выполнения. отражение можно использовать для вызова методов, которые уже реализованы во время выполнения:
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);
}
}
Reflection - это API, который используется для проверки или изменения поведения методов, классов, интерфейсов во время выполнения.
java.lang.reflect package.Пакеты java.lang и java.lang.reflect предоставляют классы для отражения Java.
Отражение можно использовать для получения информации о -
Класс Метод getClass() используется для получения имени класса, к которому принадлежит объект.
Конструкторы Метод getConstructors() используется для получения общедоступных конструкторов класса, к которому принадлежит объект.
Методы Метод getMethods() используется для получения общедоступных методов класса, к которому принадлежит объект.
Reflection API в основном используется в:
IDE (интегрированная среда разработки), например Eclipse, MyEclipse, NetBeans и т. Д.
Отладчик, инструменты тестирования и т. Д.
Преимущества использования отражения:
Возможности расширяемости: Приложение может использовать внешние, определяемые пользователем классы, создавая экземпляры объектов расширяемости, используя их полностью определенные имена.
Инструменты отладки и тестирования. Отладчики используют свойство отражения для проверки частных членов классов.
Недостатки:
Накладные расходы на производительность. Отражающие операции имеют более низкую производительность, чем их неотражающие аналоги, и их следует избегать в разделах кода, которые часто вызываются в приложениях, чувствительных к производительности.
Раскрытие внутренних компонентов: Отражающий код нарушает абстракции и, следовательно, может изменять поведение при обновлении платформы.
Ссылка: Отражение Java javarevisited.blogspot.in
В соответствии с моим пониманием:
Отражение позволяет программисту динамически получать доступ к объектам в программе. т.е. при кодировании приложения, если программист не знает о классе или его методах, он может использовать такой класс динамически (во время выполнения) с помощью отражения.
Он часто используется в сценариях, где имя класса часто меняется. Если возникает такая ситуация, то программисту сложно переписывать приложение и снова и снова менять имя класса.
Вместо этого, используя отражение, нужно беспокоиться о возможном изменении имени класса.
Отражение - это набор функций, который позволяет вам получить доступ к информации о времени выполнения вашей программы и изменить ее поведение (с некоторыми ограничениями).
Это полезно, поскольку позволяет изменять поведение среды выполнения в зависимости от метаинформации вашей программы, то есть вы можете проверить тип возвращаемого значения функции и изменить способ обработки ситуации.
В C #, например, вы можете загрузить сборку (.dll) во время выполнения и изучить ее, перемещаясь по классам и выполняя действия в соответствии с тем, что вы нашли. Он также позволяет создавать экземпляр класса во время выполнения, вызывать его метод и т. Д.
Где это может быть полезно? Полезно не каждый раз, но в конкретных ситуациях. Например, вы можете использовать его для получения имени класса для целей ведения журнала, для динамического создания обработчиков событий в соответствии с тем, что указано в файле конфигурации, и так далее ...
Я просто хочу добавить кое-что ко всему, что было перечислено.
С помощью 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'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;
}
}
Отражение - это позволить объекту увидеть свой внешний вид. Этот аргумент не имеет ничего общего с размышлениями. Фактически, это способность «самоидентифицироваться».
Само по себе отражение - это слово для таких языков, в которых отсутствует способность к самопознанию и самоощущению, как Java и C #. Поскольку они не обладают способностью к самопознанию, когда мы хотим наблюдать, как это выглядит, у нас должна быть другая вещь, чтобы поразмышлять о том, как это выглядит. Превосходные динамические языки, такие как Ruby и Python, могут воспринимать собственное отражение без помощи других людей. Можно сказать, что объект Java не может воспринимать, как он выглядит без зеркала, которое является объектом класса отражения, но объект в Python может воспринимать его без зеркала. Вот почему нам нужно отражение в Java.
Из страницы документации java.
java.lang.reflectпакет предоставляет классы и интерфейсы для получения отражающей информации о классах и объектах. Отражение обеспечивает программный доступ к информации о полях, методах и конструкторах загруженных классов, а также использование отраженных полей, методов и конструкторов для работы с их базовыми аналогами в рамках ограничений безопасности.
AccessibleObject позволяет подавить проверки доступа, если необходим ReflectPermission доступен.
Классы в этом пакете вместе с java.lang.Class поддерживают приложения, такие как отладчики, интерпретаторы, инспекторы объектов, браузеры классов и службы, такие как Object Serialization и JavaBeans, которым требуется доступ либо к открытым членам целевого объекта (на основе его класса времени выполнения), либо к члены, объявленные данным классом
Он включает следующие функции.
Просмотрите ссылку документацию для методы, предоставляемые классом Class.
Из этой статьи (написано Деннисом Сосноски, президентом Sosnoski Software Solutions, Inc) и эта статья (исследования по безопасности, pdf):
Я вижу значительные недостатки, чем использование Reflection.
Пользователь отражения:
Недостатки отражения:
Общие нарушения:
Взгляните на этот вопрос SE относительно злоупотребления функцией отражения:
Как прочитать частное поле в Java?
Резюме:
Небезопасное использование его функций, осуществляемое из системного кода, также может легко привести к компрометации режима безопасности Java l. Так что используйте эту функцию экономно
Woozle проверял другие классы при запуске, чтобы увидеть, какие из них имеют статический RegisterAsWoozleHelper() метод, и вызывать все такие методы, которые он находит, с помощью обратного вызова, который они могут использовать, чтобы сообщить Woozle о сами, избегая необходимости использовать Reflection, например десериализация данных.
- person supercat; 01.02.2020
Как следует из названия, оно отражает то, что он содержит, например, метод класса и т. Д., Помимо предоставления функции для вызова метода, создающего экземпляр динамически во время выполнения.
Он используется многими фреймворками и приложениями в лесу для вызова служб, фактически не зная кода.
Отражение дает вам возможность писать более общий код. Он позволяет вам создавать объект во время выполнения и вызывать его метод во время выполнения. Следовательно, программа может быть сильно параметризована. Это также позволяет интроспектировать объект и класс, чтобы обнаружить его переменные и метод, открытые внешнему миру.
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());
Потому что в этом случае сложно допустить опечатку.
Поскольку я считаю, что лучше всего объяснить это на примере, и ни один из ответов, похоже, этого не делает ...
Практическим примером использования отражений может быть Java Language Server, написанный на Java, или PHP Language Server, написанный на PHP, и т. Д. Language Server дает вашей IDE возможности, такие как автозаполнение, переход к определению, контекстная справка, типы подсказок и многое другое. Чтобы все имена тегов (слова, которые можно дополнить автоматически) отображали все возможные совпадения при вводе, языковой сервер должен проверять все о классе, включая блоки документов и закрытые члены. Для этого необходимо отражение указанного класса.
Другой пример - модульный тест частного метода. Один из способов сделать это - создать отражение и изменить область действия метода на общедоступную на этапе настройки теста. Конечно, можно утверждать, что частные методы не следует тестировать напрямую, но дело не в этом.
ВАЖНО
Начиная с Java 9, вы больше не можете использовать отражение, если package-info.java не откроет модуль для доступа к отражению.
По умолчанию доступ к отражению для всех пакетов в модуле запрещен.
См. Общие сведения о модулях Java 9
@Jailbreak. Он обеспечивает прямой, типобезопасный доступ к закрытым полям, методам и т. Д. Позвольте компилятору Java безопасно проверить ваш код и позволить Manifold сгенерировать для вас базовый доступ к отражению. Подробнее: manifest.systems/docs.html#type-safe-reflection - person Scott   schedule 15.12.2018