Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Глубинное клонирование объектов


Автор: Larrr 12.7.2007, 11:19
Пишу без особой надежды, но все же - есть у меня объект, в котором содержатся другие объекты из внешней библиотеки. Надо скопировать этот объект полностью - то есть включая внутренные объекты. 
1) У внутренних объектов интерфейс Cloneable не реализован и код править я не могу(уже готовая библиотека).
2) В главном объекте внутренние объекты заранее неизвестны в каком соотношении и количестве будут. Известно только 10 классов из библиотеки, которым они могут принадлежать.

Можно ли сделать универсальный глубинные клонировщик внешнего объекты с учетом 1) и 2) (универсальный в том смысле, что известен интерфейс внешнего объекта, но внутри он может содержать поля-объекты, которые я заранее не знаю, в каком количестве будут присутствовать).

Автор: LSD 12.7.2007, 11:43
Цитата(Larrr @  12.7.2007,  12:19 Найти цитируемый пост)
Можно ли сделать универсальный глубинные клонировщик внешнего объекты с учетом 1) и 2) (универсальный в том смысле, что известен интерфейс внешнего объекта, но внутри он может содержать поля-объекты, которые я заранее не знаю, в каком количестве будут присутствовать).

Что известно о внутренних объектах. У них есть конструктор по умолчанию? Или может быть они сериализуемые?

Автор: Larrr 12.7.2007, 11:57
Они несериализуемые(уже пробовала). Но во всех присутствует конструктор по умолчанию. Но он protected и я не могу его использовать(или не знаю как  smile )

Автор: ekr 12.7.2007, 12:45
Думаю, в таком случае надо нагло ломиться через reflection к сокрытым свойствам.

Автор: LSD 12.7.2007, 13:28
В простейшем случае это выглядит так:
Код
  public static Object clone(Object orig) throws IllegalAccessException, InstantiationException
  {
    if(orig == null)
      return null;
    Class clazz = orig.getClass();
    if(isImmutableOrPrimitive(clazz))
      return orig;

    Object copy = clazz.newInstance();
    while(clazz != Object.class && clazz != null)
    {
      copyFields(clazz.getDeclaredFields(), orig, copy);
      clazz = clazz.getSuperclass();
    }
    return copy;
  }

  private static void copyFields(Field[] fields, Object source, Object target) throws IllegalAccessException, InstantiationException
  {
    for(Field field : fields)
    {
      if(Modifier.isStatic(field.getModifiers()))
        continue;
      if(Modifier.isFinal(field.getModifiers()))
      {
        System.err.println("final field[" + field + "] found at " + source.getClass() + ", skipped");
        continue;
      }
      field.setAccessible(true);
      Class fieldType = field.getType();
      if(isImmutableOrPrimitive(fieldType))
      {
        Object val = field.get(source);
        field.set(target, val);
      }
      else if(fieldType.isArray())
      {
        Class componentType = fieldType.getComponentType();
        Object sourceArray = field.get(source);
        int len = Array.getLength(sourceArray);
        Object targetArray = Array.newInstance(componentType, len);
        for(int i = 0; i < len; i++)
        {
          Object val = Array.get(sourceArray, i);
          val = clone(val);
          Array.set(targetArray, i, val);
        }
        field.set(target, targetArray);
      }
      else
      {
        Object val = field.get(source);
        val = clone(val);
        field.set(target, val);
      }
    }
  }

  private static boolean isImmutableOrPrimitive(Class clazz)
  {
    return clazz.isPrimitive() ||
           clazz.isEnum() ||
           clazz == String.class ||
           clazz == Boolean.class ||
           clazz == Character.class ||
           clazz == Long.class ||
           clazz == Integer.class ||
           clazz == Short.class ||
           clazz == Byte.class ||
           clazz == Double.class ||
           clazz == Float.class ||
           clazz == Void.class;
  }

Код
ArrayList<Object> list = new ArrayList<Object>();
list.add(new Date());
list.add("Some string");
list.add(33);
list.add(new Dimension(10, 10));
ArrayList<String> sublist = new ArrayList<String>();
for(int i = 0; i < 3; i++)
  sublist.add(Integer.toString(i + 1));
list.add(sublist);

Object newList = clone(list);
System.out.println("list = " + list);
System.out.println("newList = " + newList);

Автор: Larrr 12.7.2007, 15:17
LSD, спасибо. Очень хороший понятный код   smile . 
К сожалению поля моих внешних классов все protected, плюс еще содержат свои подклассы и поэтому reflection ничего не может сделать...Вернее может, но слишком сложно.

Автор: LSD 12.7.2007, 16:00
Цитата(Larrr @  12.7.2007,  16:17 Найти цитируемый пост)
К сожалению поля моих внешних классов все protected, плюс еще содержат свои подклассы и поэтому reflection ничего не может сделать...Вернее может, но слишком сложно.

Так мой код работает даже с private полями. Единственное но, если установлен менеджер безопасности, то он может не дать выполнить setAccessible(true).

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)