410013796724260
• Webmoney
R335386147728
Z369087728698
Рефлексия кода, reflectionРефлексия (от reflexio - обращение назад) - это механизм исследования данных о программе во время её выполнения. Рефлексия в Java осуществляется с помощью Java Reflection API, состоящий из классов пакетов java.lang и java.lang.reflect. В информатике рефлексия означает процесс, во время которого программа может отслеживать и модифицировать собственную структуру и поведение во время выполнения. Java Reflection API позволяет получать информацию о конструкторах, методах и полях классов и выполнять следующие операции над полями и методами объекта/класса :
Примечание : в тексте используется объект/класс. При работе с объектом (реализацией класса) можно обращаться к полям и методам класса напрямую, если они доступны (не private). При работе с классом можно обращаться к методам класса с использованием Java Reflection API. Но класс необходимо получить из объекта. Определение свойств классаВ работающем приложении для получения класса необходимо использовать метод forName (String className). Следующий код демонстрирует возможность создания класса без использования и с использованием Reflection : // Без использования Reflection Foo foo = new Foo(); // С использованием Reflection Class foo = Class.forName("Foo"); // Загрузка JDBC-драйвера Class.forName("com.mysql.jdbc.Driver"); Метод класса forName(className) часто используется для загрузки JDBC-драйвера. Методом getName() объекта Class можно получить наименование класса, включающего пакет (package) : Class aclass = foo.getClass(); System.out.println (aclass.getName()); Для получения значения модификатора класса используется метод getModifiers(). Класс java.lang.reflect.Modifier содержит статические методы, возвращающие логическое значения проверки модификатора класса : Class cls = foo.getClass(); int mods = cls.getModifiers(); if (Modifier.isPublic (mods)) { System.out.println("public"); } if (Modifier.isAbstract(mods)) { System.out.println("abstract");} if (Modifier.isFinal (mods)) { System.out.println("final"); } Для получения суперкласса рефлексированного объекта (класса) необходимо использовать метод getSuperclass() : Class cls = foo.getClass(); Class superCls = cls.getSuperClass(); Поскольку в Java отсутствует множественное наследование, то для получения всех предков следует рекурсивно вызвать метод getSuperclass() в цикле, пока не будет достигнут Object, являющийся родителем всех классов. Object не имеет родителей, поэтому вызов его метода getSuperclass() вернет null. Определение интерфейсов и конструкторов классаДля получения в режиме run-time списка реализующих классом интерфейсов, необходимо получить Class и использовать его метод getInterfaces(). В следующем примере извлекается список интерфейсов класса ArrayList : Class<?> cls = ArrayList.class; Class<?>[] ifs = cls.getInterfaces(); System.out.println("List of interfaces\n"); for(Class<?> ifc : ifs) { System.out.println (ifc.getName()); } Чтобы IDE (Eclipse) не предупреждала о необходимости определения типа класса Class is a raw type. References to generic type Class<T> should be parameterized в коде были использованы generic'и. В консоль выводятся следующие интерфейсы, реализуемые классом ArrayList : List of interfaces java.util.List java.util.RandomAccess java.lang.Cloneable java.io.Serializable Метод класса getConstructors() позволяет получить массив открытых конструкторов типа java.lang.reflect.Constructor. После этого, можно извлекать информацию о типах параметров конструктора и генерируемых исключениях : Class<?> cls = obj.getClass(); Constructor[] constructors = cls.getConstructors(); for (Constructor constructor : constructors) { Class<?>[] params = constructor.getParameterTypes(); for (Class<?> param : params) { System.out.println(param.getName()); } } Определение полей классаМетод getFields() объекта Class возвращает массив открытых полей типа java.lang.reflect.Field, которые могут быть определены не только в данном классе, но также и в его родителях (суперклассе), либо интерфейсах, реализованных классом или его родителями. Класс Field позволяет получить имя поля, тип и модификаторы : Class<?> cls = obj.getClass(); Field[] fields = cls.getFields(); for (Field field : fields) { Class<?> fld = field.getType(); System.out.println("Class name : " + field.getName()); System.out.println("Class type : " + fld.getName()); } Если известно наименование поля, то можно получить о нем информацию с помощью метода getField() объекта Class. Class<?> cls = obj.getClass(); Field fld = cls.getField("fieldName"); Методы getField() и getFields() возвращают только открытые члены данных класса. Чтобы получить все поля класса, включая закрытые и защищенные, необходимо использовать методы getDeclaredField() и getDeclaredFields(). Данные методы работают точно так же, как и их аналоги getField() и getFields(). Определение значений полей классаКласс Field содержит специализированные методы для получения значений примитивных типов: getInt(), getFloat(), getByte() и др. Для установки значения поля, используется метод set(). Для примитивных типов имеются методы setInt(), setFloat(), setByte() и др. Class<?> cls = obj.getClass(); Field field = cls.getField("fieldName"); String value = (String) field.get(obj); field.set(obj, "New value"); Ниже приведен пример изменения значения закрытого поля класса в режиме run-time. Определение методов классаМетод getMethods() объекта Class возвращает массив открытых методов типа java.lang.reflect.Method. Эти методы могут быть определены не только в классе, но также и в его родителях (суперклассе), либо интерфейсах, реализованных классом или его родителями. Класс Method позволяет получить имя метода, тип возвращаемого им значения, типы параметров метода, модификаторы и генерируемые исключения. Class<?> cls = obj.getClass(); Method[] methods = cls.getMethods(); for (Method method : methods) { System.out.println("Method name : " + method.getName()); System.out.println("Return type : " + method.getReturnType().getName()); Class<?>[] params = method.getParameterTypes(); System.out.print("Parameters : "); for (Class<?> paramType : params) { System.out.print(" " + paramType.getName()); } System.out.println(); } Если известно имя метода и типы его параметров, то можно получить отдельный метод класса : Class<?> cls = obj.getClass(); Class[] params = new Class[] {Integer.class, String.class}; Method method = cls.getMethod("methodName", params); Пример изменения значения закрытого поля классаЧтобы изменить значение закрытого (private) поля класса необходимо получить это поле методом getDeclaredField () и вызвать метод setAccessible (true) объекта Field, чтобы открыть доступ к полю. После этого значение закрытого поля можно изменять, если оно не final. В следующем примере определен внутренний класс PrivateFinalFields с набором закрытых полей; одно из полей final. При создании объекта класса поля инициализируются. В методе main примера поочередно в закрытые поля вносятся изменения и свойства объекта выводятся в консоль. import java.lang.reflect.Field; class PrivateFinalFields { private int i = 1; private final String s = "String S"; private String s2 = "String S2"; public String toString() { return "i = " + i + ", " + s + ", " + s2; } } public class ModifyngPrivateFields { public static void main(String[] args) throws Exception { PrivateFinalFields pf = new PrivateFinalFields(); Field f = pf.getClass().getDeclaredField("i"); f.setAccessible(true); f.setInt(pf, 47); System.out.println("1. " + pf); f = pf.getClass().getDeclaredField("s"); f.setAccessible(true); f.set(pf, "MODIFY S"); System.out.println("2. " + pf); f = pf.getClass().getDeclaredField("s2"); f.setAccessible(true); f.set(pf, "MODIFY S2"); f = pf.getClass().getDeclaredField("i"); f.setAccessible(true); f.setInt(pf, 35); System.out.println("3. " + pf); } } В результате выполнения примера в консоль будут выведены следующие сообщения : 1. i = 47, String S, String S2 2. i = 47, String S, String S2 3. i = 35, String S, MODIFY S2 Из приведённого примера видно что поля private можно изменять. Для этого необходимо получить объект типа java.lang.reflect.Field с помощью метода getDeclaredField (), вызвать его метод setAccessible (true) и с помощью метода set () установить требуемое значение поля. Необходимо иметь в виду, что наличие модификатора final в закрытом текстовом поле не вызывает исключений при изменении значений, а само значение поля остаётся прежним, т.е. final поля остаются неизменные. Если не вызвать метод открытия доступа к полю setAccessible (true), то будет вызвано исключение java.lang.IllegalAccessException. Пример вызова метода, invokeJava Reflection Api позволяет вызвать метод класса. Рассмотрим пример, в котором определим класс Reflect, включающий поля и методы управления ими. В режиме run-time с помощью метода данного класса будем изменять значения полей и распечатывать их. Листинг класса ReflectКласс Reflect включает два закрытых поля (id, name) и методы управления их значениями set/get. Дополнительно в класс включим метод setData, который будем вызывать для изменения значений полей, и метод toString для печати их значений. class Reflect { private String name; private int id; Reflect() { name = "Test"; id = 999; } public int getId() { return id; } public void setId(int id) { this.id = id; } String getName() { return name; } public void setName(String name) { this.name = name; } public void setData(final int id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "Reflect [ id : " + id + ", name : " + name + "]"; } } Для тестирования объекта типа Reflect с помощью Java Reflection Api создадим класс ReflectionTest. В этот класс включим две процедуры getClassFields и getClassMethods, которые в режиме run-time распечатают всю информацию (описание полей и методов) о классе. Методы получают класс в качестве параметра. В процедурах сначала определяются массивы полей и методы; после этого их параметры распечатываются : private void getClassFields(Class<?> cls) { Field[] fields = cls.getDeclaredFields(); System.out.println("Class fields"); for (Field field : fields) { Class<?> fld = field.getType(); System.out.println("Class name : " + field.getName()); System.out.println("Class type : " + fld.getName()); } } private void getClassMethods(Class<?> cls) { Method[] methods = cls.getDeclaredMethods(); System.out.println("Class methods"); for (Method method : methods) { System.out.println("Method name : " + method.getName()); System.out.println("Return type : " + method.getReturnType().getName()); Class<?>[] params = method.getParameterTypes(); System.out.print("Parameters : "); for (Class<?> param : params) System.out.print(" " + param.getName()); System.out.println(); } } В конструкторе класса ReflectionTest сначала вызываются процедуры определения полей и методов объекта/класса Reflect. После этого вызываются методы изменения значений и печати значений с использованием Reflection API. Для определения метода setData используется массив типов параметров. Вызов метода setData выполняется с передачей ему массива новых значений. public class ReflectionTest { static Reflect reflect; public ReflectionTest() { getClassFields (reflect.getClass()); getClassMethods(reflect.getClass()); Class<?> cls = reflect.getClass(); try { System.out.println("\n1. invoke method toString()\n"); Method method = cls.getMethod("toString"); System.out.println(method.invoke(reflect)); Class<?>[] paramTypes; Object [] args; paramTypes = new Class[] {int.class, String.class}; method = cls.getMethod("setData", paramTypes); args = new Object[]{(int)123,new String("New value")}; method.invoke(reflect, args); System.out.println("\n2. invoke method toString()\n"); method = cls.getMethod("toString"); System.out.println(method.invoke(reflect)); } catch (NoSuchMethodException e) { } catch (SecurityException e) { } catch (IllegalAccessException e) { } catch (IllegalArgumentException e) { } catch (InvocationTargetException e) { } } private void getClassFields(Class<?> cls) { // код метода представлен выше } private void getClassMethods(Class<?> cls) { // код метода представлен выше } public static void main(String[] args) { reflect = new Reflect(); new ReflectionTest(); System.exit(0); } } В результате выполнения примера в консоль будут выведены представленные ниже сообщения. Методы setData и toString(), вызываемые с помощью Java Reflection API, вносят измнения в закрытые поля класса и распечатываются их значения. Class fields Class name : name Class type : java.lang.String Class name : id Class type : int Class methods Method name : toString Return type : java.lang.String Parameters : Method name : getId Return type : int Parameters : Method name : setId Return type : void Parameters : int Method name : getName Return type : java.lang.String Parameters : Method name : setName Return type : void Parameters : java.lang.String Method name : setData Return type : void Parameters : int java.lang.String 1. invoke method toString() Reflect [ id : 999, name : Test] 2. invoke method toString() Reflect [ id : 123, name : New value] Скачать примерИсходный код рассмотренного примера вызова метода invoke с использованием Java Reflection API можно скачать здесь (989 байт). |