Рефлексия в Java - несколько примеров.
Пример 1: создание объекта.
Допустим нужно создать копию заданного объекта. Какого конкретно типа объект не ясно, изначально мы знаем только то, что это высший тип в иерархии объектов Java - java.lang.Object. Если возможные типы объекта известны заранее, можно предусмотреть проверку с помощью оператора instanceof. Но предположим - типов много и предусмотреть проверку для каждого из них не представляется возможным, нужно универсальное решение. После создания копии объекта с ним могут выполняться какие-то действия, опять же с помощью рефлексии. Ведь мы не знаем, объект какого типа был передан - в этом и нет необходимости. Пример очень простой:
//создаем объект непосредственно того типа, //которым является java.lang.Object source Object newObject = source.getClass().newInstance();
Метод Class.newInstance() предполагает, что у класса, экземпляром которого является заданный объект, есть конструктор по умолчанию (без аргументов). В противном случае произойдет ошибка. Разумеется есть возможность создать класс и с помощью конструктора с аргументами. Для этого используется метод java.lang.reflect.Constructor.newInstance(). Здесь я пожалуй переадресую вас к документации Oracle - Creating New Class Instances. Итак, после создания экземпляра объекта, нужно вызвать на нем какой-то метод, который не объявлен и не определен в классе java.lang.Object, но мы то знаем - он существует (кроме того, возможно на этапе написания кода имя метода неизвестно, и определяется только во время выполнения).
Пример 2: вызов метода на объекте.
Посмотрим на метод, который позволяет вызвать на объекте любой заданный по имени метод. Если такой метод не существует, будет сгенерировано исключение NoSuchMethodException. Перехват Exception указан потому что вызов метода на объекте может порождать и другие исключения.
/** * @param object Объект для вызова * @param methodName Имя метода * @param paramsTypes Типы аргументов - необязательно * @param arg Аргументы вызываемого метода - необязательно * @return Объект, который вернул целевой метод */ private Object invokeMethod(Object object, String methodName, Class<?>[] paramsTypes, Object arg) { Object result = null; try { //получаем тип объекта, на кот. вызывается метод Class targetObjectClass = object.getClass(); //получаем метод класса Method method = findMethodAtClass(targetObjectClass, paramsTypes, methodName); //если есть аргумент if (arg != null) result = method.invoke(object, arg); else result = method.invoke(object); } catch (Exception e) { e.printStackTrace(); } return result; }
Как видно, метод позволяет вызвать любой заданный по имени метод - с аргументами или без. Нужно обратить внимание на метод findMethodAtClass. Казалось бы, мы узнали действительный тип объекта, знаем имя метода, что еще нужно?
private Method findMethodAtClass(final Class<?> type, Class<?>[] paramsTypes, final String methodName) throws NoSuchMethodException { Class<?> currentType = type; Method[] methods = type.getMethods(); //поиск метода с именем methodName в //заданном классе и всех его суперклассах while (currentType != null) { for (Method buff : methods) { if (buff.getName().equals(methodName)) { if (paramsTypes != null) { Class<?>[] params = buff.getParameterTypes(); if (Arrays.deepEquals(paramsTypes, params)) return buff; } else if (buff.getParameterTypes().length == 0) return buff; } } currentType = currentType.getSuperclass(); } throw new NoSuchMethodException(); }
Дело в том, что если метод вызывается на объекте с помощью рефлексии - нужно обязательно знать, в каком классе определен этот метод. Допустим - целевой класс является подклассом суперкласса, в котором определен нужный метод, а в самом целевом классе нет такого метода - нам нужно это знать. Получить объект Method из подкласса не получится. В примере ищется определенный метод - выше по иерархии классов, начиная с текущего (тип, которым является заданный объект).
Пример 3: доступ к полям класса.
С помощью рефлексии можно получить доступ к любому полю класса по имени.
Можно прочитать и изменить значения приватных полей, и даже final полей. Если говорить более точно - можно "на лету" сделать подобные поля открытыми для чтения или/и изменения - таким образом можно получить доступ к полю, которое было объявлено как приватное, из любого класса. В качестве примера - класс с приватным final полем name. Считываем значение поля, предварительно разрешив доступ к нему, а потом меняем значение:
Field privateStringField; try { privateStringField = testObject.getClass().getDeclaredField("name"); privateStringField.setAccessible(true); String name = (String) privateStringField.get(testObj); System.out.println("name = " + name); privateStringField.set(testObj, "Новое значение"); name = (String) privateStringField.get(testObj); System.out.println("name = " + name); } catch (Exception e) { e.printStackTrace(); }
Теги: java programming
comments powered by Disqus