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

@Override
public 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);
/*
я добавил ещё один класс Reflection
API это класс 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 заполнится всеми доступными методами,
*/
}
}