Клонирование с дженериками - PullRequest
9 голосов
/ 29 апреля 2009

Когда-то был класс:

public class Scope<C extends Cloneable & Comparable<C>> implements Comparable<Scope<C>>, Cloneable, Serializable {

   private C starts;
   private C ends;
   ...

   @SuppressWarnings("unchecked")
   @Override
   public Object clone() {
       Scope<C> scope;
       try {
           scope = (Scope<C>) super.clone();
           scope.setStarts((C) starts.clone()); // The method clone() from the type Object is not visible
           scope.setEnds((C) ends.clone()); // The method clone() from the type Object is not visible
       } catch (CloneNotSupportedException e) {
           throw new RuntimeException("Clone not supported");
       }
       return scope;
   }
}

В объекте мы имеем:

protected native Object clone() throws CloneNotSupportedException;

И Cloneable интерфейс:

public interface Cloneable { 
}

Как мне это клонировать?

Ответы [ 6 ]

10 голосов
/ 07 августа 2009

Я думаю, что текущий зеленый ответ плохой , почему вы можете спросить?

  • Это добавляет много кода
  • Требуется, чтобы вы перечислили все поля для копирования и сделали это
  • Это не будет работать для списков при использовании clone () (Это то, что clone () для HashMap говорит: возвращает поверхностную копию этого экземпляра HashMap: ключи и сами значения не клонируются.) Так что в итоге вы делаете это вручную (это заставляет меня плакать)

Да, и, кстати, сериализация тоже плохая, вам, возможно, придется добавить Serializable повсеместно (это также заставляет меня плакать).

Так, каково решение:

библиотека Java Deep-Cloning Библиотека клонирования - это небольшая Java-библиотека с открытым исходным кодом (лицензия Apache), которая глубоко клонирует объекты. Объекты не должны реализовывать интерфейс Cloneable. По сути, эта библиотека может клонировать ЛЮБЫЕ объекты Java. Его можно использовать, например, в реализациях кэша, если вы не хотите, чтобы кэшированный объект был изменен, или когда вы хотите создать глубокую копию объектов.

Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);

Проверьте это на http://code.google.com/p/cloning/

4 голосов
/ 29 апреля 2009

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

Практически единственный способ сделать это - создать собственный интерфейс с помощью public clone() метода (его не обязательно называть "clone()"). Вот пример из другого вопроса StackOverflow.

3 голосов
/ 11 февраля 2013

Надеюсь, я решил проблему общего клонирования в Java:

public class Generic<T> {
  private T data;

  public Generic() {
    // ...
  }

  @SuppressWarnings("unchecked")
  @Override
  public Object clone() {
    Generic<T> cloned = new Generic<T>();
    try {
      cloned.data = (T) data.getClass().getMethod("clone").invoke(data);
    } catch (Exception e) {
      // ...
    }
    return cloned;
  }
}
2 голосов
/ 30 апреля 2009

Немного ОТ, но вы можете спасти себя от скорби в будущем:

   catch (CloneNotSupportedException e) {
       throw new RuntimeException("Clone not supported", e);
   }

Чтобы при получении трассировки стека вы знали, какой объект вызвал проблему.

Чтобы ответить на основной вопрос, ваш собственный интерфейс, который реализует public clone (), как написал mmyers и требует, чтобы C также расширял это.

1 голос
/ 30 апреля 2009

Как общий комментарий, по возможности избегайте использования Object.clone (). Если у вас есть контроль над соответствующим кодом, используйте вместо этого конструктор копирования. См. здесь для информации.

0 голосов
/ 29 апреля 2009

Как вы видите, если класс пытается реализовать Cloneable и вам нужен клон deep , то все составляющие его объекты должны быть неизменяемыми, примитивными или также должны быть Cloneable. 1004 *

Часто лучший и простой подход - создать конструктор копирования.

public class Scope<C extends Comparable<C>> implements Comparable<Scope<C>>, Serializable {
    private C starts;
    private C ends;
    public Scope(final Scope original) {
       starts = new C(original.starts);
       ends = new C(original.ends);
       // initialize all my other fields from "original"
    }
}

и, конечно, вам нужен конструктор копирования на C, способный обрабатывать полиморфизм.

Если у вас нет доступа или возможности изменить источник на C, то любой метод копирования, независимо от того, какой это метод, будет очень сложным и потенциально невозможным. Например, невозможно сделать копию экземпляра enum.

...