Reflection API
Рефлексия (от позднелат. reflexio — обращение назад) — это механизм исследования данных о программе во время её выполнения. Рефлексия позволяет исследовать информацию о полях, методах и конструкторах классов. Сам же механизм рефлексии позволяет обрабатывать типы, отсутствующие при компиляции, но появившиеся во время выполнения программы. Рефлексия и наличие логически целостной модели выдачи информации об ошибках дает возможность создавать корректный динамический код.
public class Car {public String name;private String price;public String engType;public int engPower;public int maxSpeed;public Car(String name) {this.name = name;this.price = "1000000";this.engType = "V8";this.engPower = 123;this.maxSpeed = 100;}public int getMaxSpeed() {return maxSpeed;}public void setMaxSpeed(int maxSpeed) {this.maxSpeed = maxSpeed;}@Overridepublic String toString() {return "Car{" +"name='" + name + '\'' +", price='" + price + '\'' +", engType='" + engType + '\'' +", engPower=" + engPower +", maxSpeed=" + maxSpeed +'}';}}
public class Main {public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {Class<?> car = Class.forName("org.example.Car");/*car это экземплярпараметризированного Class, а вместо ключевого new и конструктора некий методи строка в параметре.Class это и есть основа рефлексии, именно через него мы и будем с ней работатьВообще Class создаётся при загрузке любого динамического класса и содержит информацию оего структуре! То есть как раз то что нам нужно! Объект car содержит не информацию о машине,а информацию о классе, машину описывающем! Посмотрим что можно у него узнать. Ну напримерконструктор.*/Constructor<?>[] constructors = car.getConstructors();/*метод getConstructors() возвращает список всех публичныхконструкторов объявленных в классе. Есть ещё метод getDeclaredConstructors(), онвозвращает и приватные конструкторы*/Object gaz = constructors[0].newInstance("ГАЗ");System.out.println(gaz);/*В классе Car переопределён метод toString и всё. А вот в основном классе я создалобъект gaz, но тоже не обычно. Во первых в объявлении он Object а не Car а вовторых с правой стороны не обычный конструктор а тот самый наш constructor. Тутнадо сделать уточнение, класс Constructor импортируется из пакета reflect и этозначит что, используя его методы, мы фактически пользуемся Reflection API.Метод newInstance() возвращает инициализированный объект указанного конструктора, анужные для конструктора параметры передаются в параметрах ему. В нашем случаеэто строка.*/Field[] fields = gaz.getClass().getFields();int tmp = fields[fields.length-1].getInt(gaz);fields[fields.length-1].setInt(gaz, tmp * 2);/*я добавил ещё один класс ReflectionAPI это класс Field. В нём можно хранить поля данных анализируемого нами класса.Но заполняется данными из инициализированного объекта, а значит и получить ихмы можем из объекта gaz вызвав метод getClass() и getField().Мы получили доступ к полям!Конечно можно прочесть их имя но сейчас я записал в tmp просто текущеезначение последнего поля. Для примитивных типов в классе Field определеныспециальные геттеры, чтобы привидением типов не заниматься! А для установкиновых значений есть в классе Field метод set, для примитивных типов специальныесеттеры. Им мы воспользовались для обновления значения поля maxSpeed*/System.out.println(gaz);// Но есть ещё один методField[] fields2 = gaz.getClass().getDeclaredFields();for (Field field: fields2) {if (field.getName().equals("price")) {field.setAccessible(true);field.set(gaz, "Доступно!");field.setAccessible(false);}}/*Изменений снова не так много, но они важные. Вопервых я использую методgetDeclaredField() и получаю список полей и публичных и приватных. Потом в циклеметодом getName() нахожу поле с именем "price". И вот теперь самый покаинтересный момент. Методом setAccessible() я могу сделать поле публичным, true,или снова приватным false. Это немного обходит правила инкапсуляции, нопозволяет читать и даже изменять значения любых полей объекта! Теперь мыможем в рантайме загрузить любую библиотеку, выбрать из неё нужный класс исоздать экземпляр с корректным конструктором. А также можем прочитать и дажеизменить значения всех полей объекта! Но это ещё не всё, ведь у любого классадолжны быть и методы! Давайте добавим пару методов в наш класс.*/Method[] methods = gaz.getClass().getDeclaredMethods();for (int i = 0; i < methods.length; i++) {System.out.println(methods[i]);}/*У нас появился ещё один класс из Reflection API, это класс methods. Он описываетметоды, и заполнить его можно вызвав методы getClass() и getMethods() нашегообъекта. Массив methods заполнится всеми доступными методами,*/}}