Полезное для программистов:

Фриланс
Новости
Статьи
   
Рубрики:


Java reflection

Поиск:
Reflection - это механизм, позволяющий динамически загружать и создавать экземпляры класса, а также осуществлять доступ к полям и методам класса. Для выполнения этих операций через reflection используется объект, имеющий тип Class. Этот объект содержит полную информацию, описывающую структуру класса - конструкторы, методы, поля и т.д. Объект типа Class создается автоматически Java-машиной для каждого класса, загруженного загрузчиком классов.

Для получения объекта типа Class можно воспользоваться одним из следующих способов:
Код


Class clazz = Integer.class;


или

Код

Integer number = new Integer("362");
Class clazz = number.getClass();


Получение информации о классе


В случае, если структура класса неизвестна, необходимо получить информацию о том, какие конструкторы, методы и поля содержит класс.

Код

public interface IPoint
{
public int getX();
public int getY();
public String getName();
}

public class Point implements IPoint
{
   public final static String DEFAULT_NAME = "Point";

   public static int MAX_X_VALUE = 700;
   public static int MAX_Y_VALUE = 500;

   private int x;
   private int y;
   private String name;

   public int getX()
   {
       return this.x;
   }

   private void setX(int x)
   {
       this.x = x;
   }

   public int getY()
   {
       return this.y;
   }

   private void setY(int y)
   {
       this.y = y;
   }

   public String getName()
   {
       return this.name;
   }

   protected void setName(String name)
   {
       this.name = name;
   }

   private Point(int x, int y)
   {
       this.x = x;
       this.y = y;
   }

   public Point(int x, int y, String name)
   {
       this(x, y);
       this.name = name;
   }
}

public class Main
{
   public static void main(String[] args)
   {
       Class clazz = Point.class;

       System.out.println("Class:" + clazz.getName());

       System.out.print("\nSuperclass: ");
       Class superClass = clazz.getSuperclass();
       System.out.println(superClass.getName());

       System.out.println("\nAll implemented interfaces:");
       Class[] interfaces = clazz.getInterfaces();
       for(int interfaceIndex = 0; interfaceIndex < interfaces.length; interfaceIndex++)
       {
           System.out.println(interfaces[interfaceIndex].getName());
       }

       System.out.println("\nAll declared constructors of Point class:");
       Constructor[] constructors = clazz.getDeclaredConstructors();
       for(int constructorIndex = 0; constructorIndex < constructors.length; constructorIndex++)
       {
           System.out.println(constructors[constructorIndex]);
       }

       System.out.println("\nAll declared methods of Point class:");
       Method[] methods = clazz.getDeclaredMethods();
       for(int methodIndex = 0; methodIndex < methods.length; methodIndex++)
       {
           System.out.println(methods[methodIndex]);
       }

       System.out.println("\nAll declared fields of Point class:");
       Field[] fields = clazz.getDeclaredFields();
       for(int fieldIndex = 0; fieldIndex < fields.length; fieldIndex++)
       {
           System.out.println(fields[fieldIndex]);
       }
   }
}


В примере для получения конструкторов, методов и полей класса используются методы getDeclaredConstructors(), getDeclaredMethods(), getDeclaredFields(). Но, наряду с этими методами, объект типа Class имеет также методы getConstructors(), getMethods(), getFields(). В чем их отличие? Отличаются они тем, что методы getDeclaredXXX() возвращают элементы, объявленные в текущем классе (в том числе элементы, имеющие модификаторы доступа protected и private), а методы getConstructors(), getMethods(), getFields() возвращают все public элементы текущего и родительских классов.

Часто полученной информации о структуре класса недостаточно, и возникает вопрос, как узнать модификаторы, которые имеют элементы класса. Для этих целей существует метод getModifiers() и класс Modifier с набором статических методов, позволяющих проверить наличие интересующего нас модификатора.
Код


public final class Something
{
   public transient String variable;

   public synchronized String getVariable()
   {
       return this.variable;
   }

   protected Something()
   {
       this.variable = "var";
   }
}

public class Main
{
   public static void main(String[] args)
           throws NoSuchFieldException, NoSuchMethodException
   {
       Class clazz = Something.class;

       int clazzModifiers = clazz.getModifiers();
       System.out.println("Class is final: " +Modifier.isFinal(clazzModifiers));

       Constructor constructor = clazz.getDeclaredConstructor(null);
       int constructorModifiers = constructor.getModifiers();
       System.out.println("Constructor is protected: " +Modifier.isProtected(constructorModifiers));

       Field field = clazz.getField("variable");
       int fieldModifiers = field.getModifiers();
       System.out.println("Field is transient: " +Modifier.isTransient(fieldModifiers));

       Method method = clazz.getMethod("getVariable", null);
       int methodModifiers = method.getModifiers();
       System.out.println("Method is synchronized: " +Modifier.isSynchronized(methodModifiers));
    }
}


Создание экземпляров класса. Вызов методов. Доступ к полям.

В этом разделе мы рассмотрим, как с помощью механизма reflection можно создавать экземпляры класса, вызывать методы и осуществлять доступ к полям.

Код

public class Something
{
   public static String STATIC_VARIABLE;

   public String hello;
   public String world;
   public boolean toLowerCase;

   public String printHello(String secondWord)
   {
       String result = this.hello + " " + secondWord;
       return this.toLowerCase ? result.toLowerCase() : result;
   }

   public static String concatenate(String first, int second, Double third)
   {
       return first + " " + second + " " + third;
   }

   public Something(String hello, String world, boolean toLowerCase)
   {
       this.hello = hello;
       this.world = world;
       this.toLowerCase = toLowerCase;
   }
}

public class Main
{
   public static void main(String[] args)
           throws NoSuchFieldException, IllegalAccessException,
           NoSuchMethodException, InvocationTargetException, InstantiationException
   {
       Class clazz = Something.class;

       Field staticVariable = clazz.getField("STATIC_VARIABLE");
       staticVariable.set(null, "Static Variable");
       System.out.println(staticVariable.getName() + ": " + staticVariable.get(null));

       Class[] staticMethodArgsTypes = {String.class, int.class, Double.class};
       Method staticMethod = clazz.getMethod("concatenate", staticMethodArgsTypes);

       Object[] staticMethodArgs = {new String("Book"), new Integer(33), new Double(5.32)};
       System.out.println(staticMethod.getName() + ": " + staticMethod.invoke(null, staticMethodArgs));

       Class[] constructorArgsTypes = {String.class, String.class, boolean.class};
       Constructor constructor = clazz.getConstructor(constructorArgsTypes);

       Object[] constructorArgs = {new String("Hello"), new String("World"), Boolean.TRUE};
       Object somethingInstance = constructor.newInstance(constructorArgs);

       Field hello = clazz.getField("hello");
       System.out.println(hello.getName() + ": " + hello.get(somethingInstance));

       Field world = clazz.getField("world");
       System.out.println(world.getName() + ": " + world.get(somethingInstance));

       Field toLowerCase = clazz.getField("toLowerCase");
       System.out.println(toLowerCase.getName() + ": " + toLowerCase.get(somethingInstance));

       Class[] helloMethodArgsTypes = {String.class};
       Method printHelloMethod = clazz.getMethod("printHello", helloMethodArgsTypes);
       Object[] helloMethodArgs = {new String("!!!")};
       System.out.println(printHelloMethod.getName() + ": " + printHelloMethod.invoke(somethingInstance, helloMethodArgs));
   }
}



На что следует обратить внимание:
1. При вызове статического метода или осуществлении доступа к статическому полю класса вместо экземпляра класса в методы invoke(...), get(...) и set(...) передается null.
2. Для нахождения/выполнения конструктора или метода необходимо знать, какие аргументы они имеют. Если конструктор или метод не имеет таковых, тогда передается пустой массив или null.

Изменение уровня доступа

Если конструктор, метод или поле имеют модификаторы protected или private, это еще не значит, что они не доступны. При помощи механизма reflection можно изменить уровень доступа к любому элементу класса или объекта. Для этого у полученного элемента надо вызвать метод setAccessible(true).

Код

public class Name
{
   private static String DEFAULT_NAME = "Unknown";

   private String name;

   private String getName()
   {
       return name;
   }

   private void setName(String name)
   {
       this.name = name;
   }

   private static String getDefaultName()
   {
       return DEFAULT_NAME;
   }

   private Name(String name)
   {
       this.name = name;
   }    
}

public class Main
{
   public static void main(String[] args)
           throws IllegalAccessException, InvocationTargetException,
           InstantiationException, NoSuchMethodException, NoSuchFieldException
   {
       Class clazz = Name.class;

       Field defaultNameField = clazz.getDeclaredField("DEFAULT_NAME");
       defaultNameField.setAccessible(true);

       Class[] constructorArgsTypes = {String.class};
       Constructor constructor = clazz.getDeclaredConstructor(constructorArgsTypes);
       constructor.setAccessible(true);

       Object[] constructorArgs = {defaultNameField.get(null)};
       Object nameInstance = constructor.newInstance(constructorArgs);

       Method getNameMethod = clazz.getDeclaredMethod("getName", null);
       getNameMethod.setAccessible(true);

       System.out.println(getNameMethod.invoke(nameInstance, null));

       Class[] setNameMethodArgsTypes = {String.class};
       Method setNameMethod = clazz.getDeclaredMethod("setName", setNameMethodArgsTypes);
       setNameMethod.setAccessible(true);

       Object[] setNameMethodArgs = {new String("Name")};
       setNameMethod.invoke(nameInstance, setNameMethodArgs);

       System.out.println(getNameMethod.invoke(nameInstance, null));

       Method getDefaultNameMethod = clazz.getDeclaredMethod("getDefaultName", null);
       getDefaultNameMethod.setAccessible(true);
       System.out.println(getDefaultNameMethod.invoke(null, null));        
   }
}


Работаем с массивами

Для работы с массивами в пакете java.lang.reflect предусмотрен класс Array. С его помощью можно динамически создавать массивы, определять их типы и свойства.

Код

public class LengthAndDimension
{
   public static void main(String[] args)
   {
       Object object = new double[1][2][3][4];

       boolean isArray = object.getClass().isArray();
       System.out.println("Object is Array: " + isArray);

       if(isArray)
       {
           int arrayLength = Array.getLength(object);
           System.out.println("Array length: " + arrayLength);

           int arrayDimension = getDimension(object);
           System.out.println("Array dimension: " + arrayDimension);
       }
   }

   private static int getDimension(Object array)
   {
       int dimension = 0;
       Class clazz = array.getClass();
       while(clazz.isArray())
       {
           clazz = clazz.getComponentType();
           dimension++;
       }
       return dimension;
   }
}
 


public class CreateAndExpand
{
   public static void main(String[] args)
   {
       // create arrays
       String[] stringArray1 = (String[])Array.newInstance(String.class, 5);
       String[][] stringArray2 = (String[][])Array.newInstance(String[].class, 5);
       String[][] stringArray3 = (String[][])Array.newInstance(String.class, new int[]{5, 10});

       // expand array
       String[] stringArray4 = (String[]) Array.newInstance(stringArray1.getClass()
               .getComponentType(), Array.getLength(stringArray1) + 10);
       System.arraycopy(stringArray1, 0, stringArray4, 0, Array.getLength(stringArray1));

       Array.set(stringArray4, 0, new String("Hello"));
       System.out.println("String array value of [0]: " + Array.get(stringArray4, 0));
   }
}


Proxy объекты. Динамическая связь интерфейсов


Прежде чем приступить к рассмотрению данной темы, определимся, что такое Proxy и прокси-объект.

Proxy - это паттерн, который необходимо использовать в тех случаях, когда обычного взаимодействия с объектом не достаточно, т.е. взаимодействие с ним выполняется по более сложному сценарию. Прокси-объект должен реализовывать тот же интерфейс, что и реальный объект, а также иметь доступ к реальному объекту для взаимодействия с ним. Если прокси-объект и реальный объект находятся в одной JVM, то для выполнения этого условия прокси-объекту достаточно иметь ссылку на реальный объект. Клиенты взаимодействуют с прокси-объектом, который, в свою очередь, делегирует вызовы реальному объекту, предварительно выполняя дополнительные операции.

Прокси может быть трех типов:
Virtual Proxy - применяется в тех случаях, когда создание реального объекта приводит к большим накладным расходам (например, для создания реального объекта затрачивается много времени или реальный объект требует больших ресурсо-затрат). Для этого Virtual Proxy объект выполняет инициализацию реального объекта постепенно, по требованию клиента.
Remote Proxy - удаленный прокси-объект, обеспечивает коммуникацию с реальным объектом, а также может выполнять маршелизацию и демаршелизацию отправленных и получаемых данных
Protection Proxy - используется для управления доступом к методам реального объекта.

Reflection предлагает простой и, на мой взгляд, очень гибкий способ создания прокси-объектов. В качестве примера создадим прокси-объект, который будет преобразовывать аргументы, имеющие тип String, в верхний регистр и делегировать вызовы реальному объекту.
Описание:
IName - интерфейс, реализуемый классом Name (реальный класс)
Name - реальный класс
NameProxy - прокси-класс, реализующий интерфейс InvocationHandler. В интерфейсе InvocationHandler определен один-единственный метод public Object invoke(Object proxy, Method method, Object[] args), который и является методом-"посредником" между клиентом прокси-объекта и реальным объектом, т.е. при вызове метода прокси-объекта всегда вызывается метод invoke(...), в котором необходимо определить логику взаимодействия с реальным объектом
NameProxyFactory - создает прокси-объект, имеющий тип IName

Код

public interface IName
{
   public void setName(String name);

   public String getName();
}

public class Name implements IName
{
   private String name;

   public void setName(String name)
   {
       this.name = name;
   }

   public String getName()
   {
       return this.name;
   }
}

public class NameProxy implements InvocationHandler
{
   private IName name;

   public NameProxy(IName name)
   {
       this.name = name;
   }

   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
   {
       if (args != null)
       {
           for(int argIndex = 0; argIndex < args.length; argIndex++)
           {
               if(args[argIndex] instanceof String)
               {
                   args[argIndex] = ((String)args[argIndex]).toUpperCase();
               }
           }
       }

       Object result = method.invoke(this.name, args);
       System.out.println("Method '" + method.getName() +"' has been invoked");
       return result;
   }
}

public class NameProxyFactory
{
   private final static Class[] interfaces = new Class[]{IName.class};

   public static IName createProxy()
   {
       NameProxy proxy = new NameProxy(new Name());

       return (IName) Proxy.newProxyInstance(IName.class.getClassLoader(),
               interfaces, proxy);
   }
}

public class Main
{
   public static void main(String[] args)
   {
       IName nameProxy = NameProxyFactory.createProxy();

       nameProxy.setName("Maxim");
       System.out.println(nameProxy.getName());

       nameProxy.setName("Helen");
       System.out.println(nameProxy.getName());
   }
}


На этом применение Proxy не заканчивается. Часто возникают ситуации, когда необходимо связать несколько интерфейсов в один интерфейс. Например, необходимо предоставить клиенту единый интерфейс, который бы объединил функциональность целого ряда классов, выполненных в соответствии с паттерном Command. Для решения этой задачи reflection позволяет динамически связывать интерфейсы.

Код

public interface ICommand
{
   public void execute();
}

public class PrintHello implements ICommand
{
   private String hello;

   public PrintHello(String hello)
   {
       this.hello = hello;
   }

   public void execute()
   {
       System.out.println(this.hello);
   }
}

public class PrintWorld implements ICommand
{
   private String world;

   public PrintWorld(String world)
   {
       this.world = world;
   }

   public void execute()
   {
       System.out.println(this.world);
   }
}

public interface ICommon
{
   public void printHello();
   public void printWorld();
}

public class CommonProxy implements InvocationHandler
{
   private ICommand hello;
   private ICommand world;

   public CommonProxy(ICommand hello, ICommand world)
   {
       this.hello = hello;
       this.world = world;
   }

   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
   {
       if(method.getName().equals("printHello"))
       {
           this.hello.execute();
       }
       else if(method.getName().equals("printWorld"))
       {
           this.world.execute();
       }
       else
       {
           throw new RuntimeException("Method name=" + method.getName()+ " not implemented yet.");
       }
       return null;
   }
}

public class CommonProxyFactory
{
   private final static Class[] interfaces = new Class[]{ICommon.class};

   public static ICommon createProxy()
   {
       ICommand hello = new PrintHello("Hello");
       ICommand world = new PrintHello("World");

       CommonProxy proxy = new CommonProxy(hello, world);

       return (ICommon) Proxy.newProxyInstance(ICommon.class.getClassLoader(), interfaces, proxy);
   }
}

public class Main
{
   public static void main(String[] args)
   {
       ICommon common = CommonProxyFactory.createProxy();

       common.printHello();
       common.printWorld();
   }
}


Ну и напоследок, усовершенствуем предыдущий пример.

Код

public interface ICommand
{
   public Method getMethod() throws NoSuchMethodException;
   public Object execute(Object[] args);
}

public class ConcatenateCommand implements ICommand
{
   public Method getMethod() throws NoSuchMethodException
   {
       Class[] types = new Class[]{String.class, String.class};
       return ICommon.class.getMethod("concatenate", types);
   }

   public Object execute(Object[] args)
   {
       return (String)args[0] + args[1];
   }
}

public class SumCommand implements ICommand
{
   public Method getMethod() throws NoSuchMethodException
   {
       Class[] types = new Class[]{int.class, int.class, int.class};
       return ICommon.class.getMethod("sum", types);
   }

   public Object execute(Object[] args)
   {
       int result = ((Integer)args[0]).intValue()
               + ((Integer)args[1]).intValue() + ((Integer)args[2]).intValue();

       return Integer.valueOf(result);
   }
}

public interface ICommon
{
   public String concatenate(String valueOne, String valueTwo);

   public int sum(int valueOne, int valueTwo, int valueThree);
}

public class CommonProxy implements InvocationHandler
{
   private Map commands;

   public CommonProxy()
   {
       this.commands = new HashMap();
   }

   public void addCommand(ICommand command) throws NoSuchMethodException
   {
       if(!this.commands.containsValue(command))
       {
           this.commands.put(command.getMethod(), command);
       }
   }

   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
   {
       Object result;

       ICommand command = (ICommand) this.commands.get(method);
       if(command != null)
       {
           result = command.execute(args);
       }
       else
       {
           throw new RuntimeException("Method name="
                   + method.getName()+ " not implemented yet.");
       }

       return result;
   }
}

public class CommonProxyFactory
{
   private final static Class[] interfaces = new Class[]{ICommon.class};

   public static ICommon createProxy() throws NoSuchMethodException
   {
       CommonProxy proxy = new CommonProxy();
       proxy.addCommand(new ConcatenateCommand());
       proxy.addCommand(new SumCommand());

       return (ICommon) Proxy.newProxyInstance(ICommon.class.getClassLoader(), interfaces, proxy);
   }
}

public class Main
{
   public static void main(String[] args)
           throws NoSuchMethodException
   {
       ICommon common = CommonProxyFactory.createProxy();

       System.out.println(common.concatenate("Hello ", "World"));
       System.out.println(common.sum(7, 3, 7));
   }
}
Автор: maximb






Просмотров: 9384

 

 

Новые статьи:


Популярные:
  1. Как сделать цикличным проигрывание MIDI-файла?
  2. Создание AVI файла из рисунков
  3. Как устройство "отключить в данной конфигурации"?
  4. Kто в данный момент присоединен через Сеть?
  5. Как узнать количество доступной памяти?
  6. Как реализовать в RichEdit разноцветный текст?
  7. Как скрыть свое приложение от ProcessViewer
  8. Как программно нажать/скрыть/показ кнопку "Start"?
  9. Модуль работы с ресурсами в PE файлах
10. Функции вызова диалоговых окон выбора
11. Проверка граматики средствами Word'а из Delphi.
12. Модуль для упрощенного вызова сообщений
13. Функции для записи и чтение своих данных в, ЕХЕ- файле
14. Рекурсивный просмотр директорий
15. Network Traffic Monitor
16. Разные модули
17. Универсальная функция для обращения к любым экспортируем функциям DLL
18. Библиотека от VladS
19. Протектор для UPX'а
20. Еще об ICQ, сообщения по контакт листу?
21. Использование открытых интерфейсов
22. Теория и практика использования RTTI
23. Работа с TApplication
24. Примеры использования Drag and Drop для различных визуальных компонентов
25. Что такое порт? Правила для работы с портами
26. Симфония на клавиатуре
27. Загрузка DLL
28. Исправление автоинкремента
29. Взаимодействие с чужими окнами
30. Проверить дубляжи в столбце


 

 

 
 
На главную