Вопрос: Что такое отражение и почему оно полезно?


Что такое отражение и почему оно полезно?

Я особенно интересуюсь Java, но я предполагаю, что принципы одинаковы на любом языке.


1650


источник


Ответы:


Отражение названия используется для описания кода, который способен проверять другой код в той же системе (или сам).

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


1380



отражение это способность языка проверять и динамически вызывать классы, методы, атрибуты и т. д. во время выполнения.

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

Отражение важно, поскольку оно позволяет вам писать программы, которые не должны «знать» все во время компиляции, делая их более динамичными, поскольку их можно связать во время выполнения. Код может быть написан против известных интерфейсов, но фактические классы, которые будут использоваться, могут быть созданы с использованием отражения от файлов конфигурации.

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


192



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

82



Использование отражений

Отражение обычно используется программами, которые требуют возможности исследовать или изменять поведение среды выполнения приложений, запущенных на виртуальной машине Java. Это относительно продвинутая функция и должна использоваться только разработчиками, которые хорошо понимают основы языка. Учитывая это предостережение, рефлексия является мощным методом и может позволить приложениям выполнять операции, которые в противном случае были бы невозможны.

Возможности расширения

Приложение может использовать внешние пользовательские классы, создавая экземпляры объектов расширяемости, используя их полностью квалифицированные имена. Браузеры классов и среды визуальной разработки Браузер класса должен иметь возможность перечислять члены классов. Визуальные среды разработки могут извлечь выгоду из использования информации типа, доступной для размышления, чтобы помочь разработчику в написании правильного кода. Отладчики и средства тестирования Отладчики должны уметь проверять частных членов на классах. Тестовые жгуты могут использовать рефлексию для систематического вызова API-интерфейсов обнаружения, определенных в классе, для обеспечения высокого уровня охвата кода в наборе тестов.

Недостатки отражения

Отражение мощно, но не должно использоваться без разбора. Если можно выполнить операцию без использования отражения, то предпочтительно избегать ее использования. Следующие проблемы следует учитывать при доступе к коду через отражение.

  • Производительность

Поскольку отражение включает типы, которые динамически разрешены, некоторые оптимизации виртуальной машины Java не могут быть выполнены. Следовательно, рефлексивные операции имеют более низкую производительность, чем их неотражающие аналоги, и их следует избегать в разделах кода, которые часто называются приложениями, чувствительными к характеристикам.

  • Ограничения безопасности

Для отражения требуется разрешение во время выполнения, которое может отсутствовать при работе под управлением безопасности. Это важно для кода, который должен выполняться в ограниченном контексте безопасности, например, в апплете.

  • Воздействие внутренних

Поскольку отражение позволяет коду выполнять операции, которые были бы незаконными в неотражающем коде, например, при доступе к частным полям и методам, использование рефлексии может привести к неожиданным побочным эффектам, что может привести к дисфункции кода и может привести к повреждению переносимости. Отражающий код разбивает абстракции и поэтому может изменять поведение при обновлении платформы.

источник: API Reflection


52



Отражение - ключевой механизм, позволяющий приложению или каркасу работать с кодом, который, возможно, еще не был написан!

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

Другим примером может служить Java API для анализа XML (JAXP) , Где поставщик XML-парсеров «подключен» через известные системные свойства, которые используются для построения новых экземпляров посредством отражения.

И, наконец, наиболее полным примером является весна который использует отражение для создания своих бобов и для его интенсивного использования прокси


29



Не каждый язык поддерживает размышления, но принципы обычно одинаковы на языках, которые его поддерживают.

Отражение - это способность «отражать» структуру вашей программы. Или более конкретный. Чтобы просмотреть объекты и классы, которые у вас есть, и программно получить информацию о методах, полях и интерфейсах, которые они реализуют. Вы также можете посмотреть на такие вещи, как аннотации.

Это полезно во многих ситуациях. Всюду вы хотите динамически подключать классы к вашему коду. Лоты объектных реляционных карт используют отражение, чтобы иметь возможность создавать объекты из баз данных, не зная заранее, какие объекты они будут использовать. Плагины - это еще одно место, где полезно использовать отражение. Возможность динамически загружать код и определять, существуют ли там типы, которые реализуют правильный интерфейс для использования в качестве плагина, важны в этих ситуациях.


26



Reflection позволяет создавать новые объекты, вызывать методы и получать / устанавливать операции над переменными класса динамически во время выполнения без предварительного знания его реализации.

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, но он может замедлить работу приложения, если он используется в избытке, поскольку он разрешает все типы во время выполнения.


25



Example :
Take for example a remote application which gives your application an object which you obtain using their API Methods . Now based on the object you might need to perform some sort of computation .
The provider guarantees that object can be of 3 types and we need to perform computation based on what type of object .
So we might implement in 3 classes each containing a different logic .Obviously the object information is available in runtime so you cannot statically code to perform computation hence reflection is used to instantiate the object of the class that you require to perform the computation based on the object received from the provider .


17



Java Reflection is quite powerful and can be very useful. Java Reflection makes it possible to inspect classes, interfaces, fields and methods at runtime, without knowing the names of the classes, methods etc. at compile time. It is also possible to instantiate new objects, invoke methods and get/set field values using reflection.

A quick Java Reflection example to show you what using reflection looks like:

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

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

This example obtains the Class object from the class called MyObject. Using the class object the example gets a list of the methods in that class, iterates the methods and print out their names.

Exactly how all this works is explained here

Edit: After almost 1 year I am editing this answer as while reading about reflection I got few more uses of Reflection.

  • Spring uses bean configuration such as:


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

When the Spring context processes this < bean > element, it will use Class.forName(String) with the argument "com.example.Foo" to instantiate that Class.

It will then again use reflection to get the appropriate setter for the < property > element and set its value to the specified value.

  • Junit uses Reflection especially for testing Private/Protected methods.

For Private methods,

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

For private fields,

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

15



simple example for reflection. In a Chess Game, you do not know what will be moved by the user at run time. reflection can be used to call methods which are already implemented at run time.

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

}

11



Reflection is a set of functions which allows you to access the runtime information of your program and modify it behavior (with some limitations).

It's useful because it allows you to change the runtime behaivour depending on the meta information of your program, that is, you can check the return type of a function and change the way you handle the situation.

In C# for example you can load an assembly (a .dll) in runtime an examine it, navigating through the classes and taking actions according to what you found. It also let you create an instance of a class on runtime, invoke its method, etc.

Where can it be useful? Is not useful everytime but for concrete situations. For example you can use it to get the name of the class for loggin purposes, to dinamically create handlers for events according to what's specified on a configuration file and so on...


10



As per my understanding:

Reflection allows programmer to access entities in program dynamically. i.e. while coding an application if programmer is unaware about a class or its methods, he can make use of such class dynamically (at run time) by using reflection.

It is frequently used in scenarios where a class name changes frequently. If such a situation arises, then it is complicated for the programmer to rewrite the application and change the name of the class again and again.

Instead, by using reflection, there is need to worry about a possibly changing class name.


10