Что такое Reflection?
Рефлексия — это API, который позволяет создавать экземпляры нужного класса через объект типа Class, загруженный в кучу, а также получать доступ к полям и методам экземпляра независимо от модификаторов доступа.
Здесь «загруженный класс» означает, что загрузчик классов JVM завершил загрузку файла класса и создал объект типа Classсодержащий информацию о данном классе, и сохранил его в куче. Обратите внимание, что это отличается от объекта, создаваемого с помощью ключевого слова new. Если вам не понятен объект типа Class, ознакомьтесь с документацией JDK для java.lang.class.
Способ использования
Прежде чем использовать рефлексию, необходимо получить объект типа Class, загруженный в кучу. Существует три способа сделать это.
- Получение с помощью Class.class
- Получение с помощью Instance.getClass()
- Получение с помощью Class.forName("Имя класса")
Можно убедиться, что экземпляры типа Class, полученные тремя способами, идентичны. Хэш-коды одинаковы независимо от используемого способа, поэтому следует использовать подходящий способ в зависимости от ситуации.
Теперь, используя полученный тип Class, мы можем создавать экземпляры данного класса и получать доступ к его полям и методам независимо от модификаторов доступа. Давайте начнем с создания экземпляра класса.
С помощью getConstructor() можно получить конструктор, а с помощью newInstance() — динамически создать экземпляр Member.
Наконец, давайте получим доступ к полям и методам экземпляра независимо от модификаторов доступа.
С помощью getDeclaredFileds() можно получить все переменные экземпляра класса, а с помощью get() — вернуть значение поля, а с помощью set() — изменить значение поля. При этом важно помнить, что при доступе к полю с модификатором доступа private необходимо передать в setAccessible() значение true.
Методы также можно получить с помощью getDeclaredMethod(). При этом в качестве аргументов необходимо передать имя метода и тип его параметров. Аналогично, при доступе к методу с модификатором доступа private необходимо установить для setAccessible() значение true. Наконец, с помощью метода invoke() можно вызвать метод, полученный с помощью API рефлексии.
Преимущества и недостатки
- Преимущества
- Гибкость: позволяет во время выполнения создавать экземпляры классов и получать доступ к полям и методам независимо от модификаторов доступа, выполняя необходимые действия.
- Недостатки
- Нарушение инкапсуляции.
- Поскольку экземпляр создается во время выполнения, тип нельзя проверить во время компиляции.
- Поскольку экземпляр создается во время выполнения, сложно отследить конкретный ход выполнения программы.
- Доступ к полям и методам с помощью рефлексии может быть медленнее, чем прямой доступ (не во всех случаях).
Причины использования
API рефлексии позволяет во время выполнения получать доступ к информации о классах и управлять ими. Даже поля и методы с модификатором доступа private можно изменять. Поскольку это нарушает инкапсуляцию, которая является важной частью объектно-ориентированного проектирования, может показаться, что эту технологию лучше не использовать.
В небольших консольных приложениях разработчик может легко определить все используемые объекты и зависимости во время компиляции. Однако в крупных проектах, таких как фреймворки, отследить большое количество объектов и зависимостей сложно. В таких случаях рефлексия позволяет динамически создавать классы и устанавливать между ними зависимости.
Например, в Spring Bean Factory, если вы добавите аннотации @Controller, @Service, @Repository, Bean Factory автоматически создаст и будет управлять классами с этими аннотациями. Разработчик не передавал эти классы в Bean Factory, но это возможно благодаря рефлексии. Во время выполнения Bean Factory ищет классы с указанными аннотациями, и если находит их, то с помощью рефлексии создает экземпляры этих классов, вводит необходимые поля и сохраняет их в Bean Factory.
Конечно, как уже упоминалось, рефлексия нарушает инкапсуляцию, поэтому использовать ее следует только в необходимых случаях.
Комментарии0