Рефлексия java зачем нужна

Trail: The Reflection API

Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.

Extensibility Features An application may make use of external, user-defined classes by creating instances of extensibility objects using their fully-qualified names. Class Browsers and Visual Development Environments A class browser needs to be able to enumerate the members of classes. Visual development environments can benefit from making use of type information available in reflection to aid the developer in writing correct code. Debuggers and Test Tools Debuggers need to be able to examine private members on classes. Test harnesses can make use of reflection to systematically call a discoverable set APIs defined on a class, to insure a high level of code coverage in a test suite.

Drawbacks of Reflection

Reflection is powerful, but should not be used indiscriminately. If it is possible to perform an operation without using reflection, then it is preferable to avoid using it. The following concerns should be kept in mind when accessing code via reflection.

Performance Overhead Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications. Security Restrictions Reflection requires a runtime permission which may not be present when running under a security manager. This is in an important consideration for code which has to run in a restricted security context, such as in an Applet. Exposure of Internals Since reflection allows code to perform operations that would be illegal in non-reflective code, such as accessing private fields and methods, the use of reflection can result in unexpected side-effects, which may render code dysfunctional and may destroy portability. Reflective code breaks abstractions and therefore may change behavior with upgrades of the platform.

Читайте также:  Javascript form action javascript function

Trail Lessons

This trail covers common uses of reflection for accessing and manipulating classes, fields, methods, and constructors. Each lesson contains code examples, tips, and troubleshooting information.

Classes This lesson shows the various ways to obtain a Class object and use it to examine properties of a class, including its declaration and contents. Members This lesson describes how to use the Reflection APIs to find the fields, methods, and constructors of a class. Examples are provided for setting and getting field values, invoking methods, and creating new instances of objects using specific constructors. Arrays and Enumerated Types This lesson introduces two special types of classes: arrays, which are generated at runtime, and enum types, which define unique named object instances. Sample code shows how to retrieve the component type for an array and how to set and get fields with array or enum types.

The examples in this trail are designed for experimenting with the Reflection APIs. The handling of exceptions therefore is not the same as would be used in production code. In particular, in production code it is not recommended to dump stack traces that are visible to the user.

Источник

Reflection API. Рефлексия. Темная сторона Java

Java-университет

Рефлексия в Java осуществляется с помощью Java Reflection API. Что такое эта рефлексия? Существует короткое и точное, а также популярное на просторах интернета определение. Рефлексия (от позднелат. reflexio — обращение назад) — это механизм исследования данных о программе во время её выполнения. Рефлексия позволяет исследовать информацию о полях, методах и конструкторах классов. Сам же механизм рефлексии позволяет обрабатывать типы, отсутствующие при компиляции, но появившиеся во время выполнения программы. Рефлексия и наличие логически целостной модели выдачи информации об ошибках дает возможность создавать корректный динамический код. Иначе говоря, понимание принципов работы рефлексии в java открывает перед вами ряд удивительных возможностей. Вы буквально можете жонглировать классами и их составляющими.

Reflection API. Рефлексия. Темная сторона Java - 2

  • Узнать/определить класс объекта;
  • Получить информацию о модификаторах класса, полях, методах, константах, конструкторах и суперклассах;
  • Выяснить, какие методы принадлежат реализуемому интерфейсу/интерфейсам;
  • Создать экземпляр класса, причем имя класса неизвестно до момента выполнения программы;
  • Получить и установить значение поля объекта по имени;
  • Вызвать метод объекта по имени.
 public class MyClass < private int number; private String name = "default"; // public MyClass(int number, String name) < // this.number = number; // this.name = name; // >public int getNumber() < return number; >public void setNumber(int number) < this.number = number; >public void setName(String name) < this.name = name; >private void printData() < System.out.println(number + name); >> 

Как мы видим, это самый обычный класс. Конструктор с параметрами закомментирован не просто так, мы к этому еще вернемся. Если вы внимательно просмотрели содержимое класса, то наверняка увидели отсутствие getter ’a для поля name . Само поле name помечено модификатором доступа private , обратиться к нему вне самого класса у нас не выйдет => мы не можем получить его значение. “Так в чем проблема? — скажете вы. — Допиши getter или измени модификатор доступа”. И вы будете правы, но, что если MyClass находится в скомпилированной aar библиотеке или в другом закрытом модуле без доступа к редактированию, а на практике такое случается крайне часто. И какой-то невнимательный программист просто забыл написать getter . Самое время вспомнить о рефлексии! Попробуем добраться до private поля name класса MyClass :

 public static void main(String[] args) < MyClass myClass = new MyClass(); int number = myClass.getNumber(); String name = null; //no getter =( System.out.println(number + name);//output 0null try < Field field = myClass.getClass().getDeclaredField("name"); field.setAccessible(true); name = (String) field.get(myClass); >catch (NoSuchFieldException | IllegalAccessException e) < e.printStackTrace(); >System.out.println(number + name);//output 0default > 

Разберем что тут сейчас произошло. В java есть замечательный класс Class . Он представляет классы и интерфейсы в исполняемом приложении Java. Связь между Class и ClassLoader мы затрагивать не будем, т.к. это не есть тема статьи. Далее, чтобы получить поля этого класса нужно вызвать метод getFields() , этот метод вернет нам все доступные поля класса. Нам это не подходит, так как наше поле private , поэтому используем метод getDeclaredFields() , этот метод также возвращает массив полей класса, но теперь и private и protected . В нашей ситуации мы знаем имя поля, которое нас интересует, и можем использовать метод getDeclaredField(String) , где String — имя нужного поля. Примечание: getFields() и getDeclaredFields() не возвращают поля класса-родителя! Отлично, мы получили объект Field с ссылкой на наш name . Т.к. поле не было публичным (public) следует дать доступ для работы с ним. Метод setAccessible(true) разрешает нам дальнейшую работу. Теперь поле name полностью под нашим контролем! Получить его значение можно вызовом get(Object) у объекта Field , где Object — экземпляр нашего класса MyClass . Приводим к типу String и присваиваем нашей переменной name . На тот случай если у нас вдруг не оказалось setter ’a, для установки нового значения полю name можно использовать метод set :

 field.set(myClass, (String) "new value"); 

Поздравляю! Вы только что овладели базовым механизмом рефлексии и смогли получить доступ к private полю! Обратите внимание на блок try/catch и типы обрабатываемых исключений. IDE сама укажет на их обязательное присутствие, но по их названию итак ясно зачем они здесь. Идем дальше! Как вы могли заметить, наш MyClass уже имеет метод для вывода информации о данных класса:

Но этот программист и тут наследил. Метод находится под модификатором доступа private , и нам пришлось самим каждый раз писать код вывода. Не порядок, где там наша рефлексия?… Напишем вот такую функцию:

 public static void printData(Object myClass) < try < Method method = myClass.getClass().getDeclaredMethod("printData"); method.setAccessible(true); method.invoke(myClass); >catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) < e.printStackTrace(); >> 

Здесь примерно такая же процедура как и с получением поля — получаем нужный метод по имени и даем доступ к нему. И для вызова объекта Method используем invoke(Оbject, Args) , где Оbject — все также экземпляр класса MyClass . Args — аргументы метода — наш таковых не имеет. Теперь для вывода информации мы используем функцию printData :

 public static void main(String[] args) < MyClass myClass = new MyClass(); int number = myClass.getNumber(); String name = null; //? printData(myClass); // outout 0default try < Field field = myClass.getClass().getDeclaredField("name"); field.setAccessible(true); field.set(myClass, (String) "new value"); name = (String) field.get(myClass); >catch (NoSuchFieldException | IllegalAccessException e) < e.printStackTrace(); >printData(myClass);// output 0new value > 

Ура, теперь у нас есть доступ к приватному методу класса. Но что делать если у метода все таки будут аргументы, и зачем тот закомментированный конструктор? Всему свое время. Из определения в начале ясно, что рефлексия позволяет создавать экземпляры класса в режиме runtime (во время выполнения программы)! Мы можем создать объект класса по полному имени этого класса. Полное имя класса — это имя класса, учитывая путь к нему в package .

Reflection API. Рефлексия. Темная сторона Java - 3

В моей иерархии package полным именем MyClass будет “ reflection.MyClass ”. Также узнать имя класса можно простым способом (вернет имя класса в виде строки):

 public static void main(String[] args) < MyClass myClass = null; try < Class clazz = Class.forName(MyClass.class.getName()); myClass = (MyClass) clazz.newInstance(); >catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) < e.printStackTrace(); >System.out.println(myClass);//output created object reflection.MyClass@60e53b93 > 

На момент старта java приложения далеко не все классы оказываются загруженными в JVM. Если в вашем коде нет обращения к классу MyClass , то тот, кто отвечает за загрузку классов в JVM, а им является ClassLoader , никогда его туда и не загрузит. Поэтому нужно заставить ClassLoader загрузить его и получить описание нашего класса в виде переменной типа Class . Для этой задачи существует метод forName(String) , где String — имя класса, описание которого нам требуется. Получив Сlass , вызов метода newInstance() вернет Object , который будет создан по тому самому описанию. Остается привести этот объект к нашему классу MyClass . Круто! Было сложно, но, надеюсь, понятно. Теперь мы умеем создавать экземпляр класса буквально из одной строки! К сожалению описанный способ будет работать только с конструктором по умолчанию (без параметров). Как же вызывать методы с аргументами и конструкторы с параметрами? Самое время раскомментировать наш конструктор. Как и ожидалось, newInstance() не находит конструктор по умолчанию и больше не работает. Перепишем создание экземпляра класса:

 public static void main(String[] args) < MyClass myClass = null; try < Class clazz = Class.forName(MyClass.class.getName()); Class[] params = ; myClass = (MyClass) clazz.getConstructor(params).newInstance(1, "default2"); > catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) < e.printStackTrace(); >System.out.println(myClass);//output created object reflection.MyClass@60e53b93 > 

Для получения конструкторов класса следует у описания класса вызвать метод getConstructors() , а для получения параметров конструктора — getParameterTypes() :

 Constructor[] constructors = clazz.getConstructors(); for (Constructor constructor : constructors) < Class[] paramTypes = constructor.getParameterTypes(); for (Class paramType : paramTypes) < System.out.print(paramType.getName() + " "); >System.out.println(); > 

Таким образом получаем все конструкторы и все параметры к ним. В моем примере идет обращение к конкретному конструктору с конкретными уже известными параметрами. И для вызова этого конструктора используем метод newInstance , в котором указываем значения этим параметрам. Точно так же будет и с invoke для вызова методов. Возникает вопрос: где может пригодится рефлексивный вызов конструкторов? Современные технологии java, как уже говорилось в начале, не обходятся без Reflection API. Например, DI (Dependency Injection), где аннотации в сочетании с рефлексией методов и конструкторов образуют популярную в Android разработке библиотеку Dagger. После прочтения этой статьи вы с уверенностью можете считать себя просвещенным в механизмы Reflection API. Темной стороной java рефлексия называется не зря. Она напрочь ломает парадигму ООП. В java инкапсуляция служит для сокрытия и ограничения доступа одних компонентов программы к другим. Используя модификатор private мы подразумеваем, что доступ к этому полю будет только в пределах класса, где это поле существует, основываясь на этом мы строим дальнейшую архитектуру программы. В этой статье мы увидели, как с помощью рефлексии можно пробираться куда угодно. Хорошим примером в виде архитектурного решения является порождающий шаблон проектирования — Singleton . Основная его идея в том, чтобы на протяжении всей работы программы класс, реализующий этот шаблон был только в одном экземпляре. Осуществляется это при помощи установки конструктору по умолчанию private модификатор доступа. И будет очень нехорошо, если какой-то программист со своей рефлексией будет плодить такие классы. Кстати, есть очень интересный вопрос, который я недавно услышал от своего сотрудника: может ли быть у класса, реализующий шаблон Singleton , наследники? Неужели в этом случае бессильна даже рефлексия? Пишите ваши feedback’и по статье и ответ в коментарии, а также задавайте свои вопросы! Истинная Сила Reflection API раскрывается в комбинации c Runtime Annotations, о чем мы, возможно, поговорим в следующей статье про темную сторону Java. Спасибо за внимание!

Источник

Оцените статью