Java: рекомендуемое решение для глубокого клонирования/копирования экземпляра
Мне интересно, есть ли рекомендуемый способ сделать глубокий клон/копию экземпляра в java.
У меня есть 3 решения, но я могу пропустить их, и я хотел бы получить ваше мнение
edit: включить Bohzo offerтон и уточнить вопрос: это больше о глубоком клонировании, чем мелкое клонирование.
Сделайте это самостоятельно:
закодируйте клон по свойствам вручную после свойств и проверьте, что клонируемые экземпляры тоже клонированы.
про:
- контроль над тем, что будет выполнено
- быстрое выполнение
минусы:
- утомительно писать и поддерживать
- ошибка (ошибка копирования/вставки, отсутствие свойства, переназначенное изменяемое свойство)
Использовать отражение:
С помощью собственных инструментов отражения или внешнего помощника (например, jakarta common- beans) легко написать общий метод копирования, который будет выполнять задание в одной строке.
про:
- легко писать
- нет обслуживания
минусы:
- меньше контроля над тем, что происходит
- ошибка, подверженная изменяемому объекту, если инструмент отражения не клонирует вспомогательные объекты тоже
- медленное выполнение
Использование рамки клонирования:
Используйте фреймворк, который сделает это за вас, например:
commons-lang SerializationUtils
Библиотека глубокого клонирования Java
Dozer
Kryo
про:
- то же, что отражение
- больше контроля над тем, что именно будет клонировано.
минусы:
- каждый измененный экземпляр полностью клонирован, даже в конце иерархии
- может быть очень медленным выполнение
Использовать инструментарий байт-кода для записи клона во время выполнения
javassit, BCEL или cglib может использоваться для генерации выделенного клонера так же быстро, как одна из поданных рук. Кто-то знает lib, используя один из этих инструментов для этой цели?
Что я здесь пропустил?
Какой из них вы бы порекомендовали?
Спасибо.
Ответы
Ответ 1
Для глубокого клонирования (клонирует всю иерархию объектов):
-
commons-lang SerializationUtils - используя сериализацию - если все классы находятся под вашим контролем, и вы можете принудительно реализовать Serializable
.
-
Библиотека глубокого клонирования Java - с использованием отражения - в случаях, когда классы или объекты, которые вы хотите клонировать, находятся вне вашего контроля (сторонняя библиотека), и вы не можете заставить их реализовать Serializable
, или в случаях, когда вы не хотите внедрять Serializable
.
Для мелкого клонирования (клонирует только свойства первого уровня):
Я специально упустил опцию "сделай сам" - вышеприведенный API обеспечивает хороший контроль над тем, что нужно и что не клонировать (например, используя transient
или String[] ignoreProperties
), поэтому изобретать колесо isn Предпочтительно.
Ответ 2
В книге Джошуа Блоха есть целая глава, озаглавленная "Пункт 10: Надлежащее клонирование разумно" , в котором он переходит к тому, что, по большому счету, это плохая идея, потому что спецификация Java для нее создает много проблем.
Он предоставляет несколько альтернатив:
-
Используйте конструктор factory вместо конструктора:
public static Yum newInstance(Yum yum);
-
Используйте конструктор копирования:
public Yum(Yum yum);
Все классы коллекции в Java поддерживают конструктор копирования (например, новый ArrayList (l);)
Ответ 3
Начиная с версии 2.07 Kryo поддерживает мелкое/глубокое клонирование:
Kryo kryo = new Kryo();
SomeClass someObject = ...
SomeClass copy1 = kryo.copy(someObject);
SomeClass copy2 = kryo.copyShallow(someObject);
Kryo быстро, на странице и на их странице вы можете найти список компаний, которые используют его в производстве.
Ответ 4
Используйте XStream toXML/fromXML в памяти. Чрезвычайно быстрая и долгое время существует и идет крепко. Объекты не обязательно должны быть Serializable, и у вас нет рефлекса использования (хотя XStream делает). XStream может различать переменные, указывающие на один и тот же объект, и не случайно создавать две полные копии экземпляра. С тех пор многие детали были забиты. Я использовал его в течение нескольких лет, и это идет. Это примерно так же просто, как вы можете себе представить.
new XStream().toXML(myObj)
или
new XStream().fromXML(myXML)
Чтобы клонировать,
new XStream().fromXML(new XStream().toXML(myObj))
Более кратко:
XStream x = new XStream();
Object myClone = x.fromXML(x.toXML(myObj));
Ответ 5
Я бы рекомендовал метод DIY, который в сочетании с хорошим методом hashCode() и equals() должен быть легко доказан в unit test.
Ответ 6
Я бы предложил переопределить Object.clone(), сначала вызвать super.clone(), а затем вызвать ref = ref.clone() для всех ссылок, которые вы хотите скопировать. Он более или менее делает это самостоятельно, но требует немного меньше кодирования.
Ответ 7
Зависит.
Для скорости используйте DIY.
Для пуленепробиваемых используйте отражение.
BTW, сериализация не такая же, как refl, поскольку некоторые объекты могут предоставлять переопределенные методы сериализации (readObject/writeObject), и они могут быть ошибочными
Ответ 8
Для сложных объектов и при невысокой производительности я использую gson
для сериализации объекта в json-тексте, затем десериализуйте текст, чтобы получить новый объект.
gson, который на основе отражения будет работать в большинстве случаев, за исключением того, что поля transient
не будут скопированы, а объекты с круговой ссылкой с причиной StackOverflowError
.
public static <ObjectType> ObjectType Copy(ObjectType AnObject, Class<ObjectType> ClassInfo)
{
Gson gson = new GsonBuilder().create();
String text = gson.toJson(AnObject);
ObjectType newObject = gson.fromJson(text, ClassInfo);
return newObject;
}
public static void main(String[] args)
{
MyObject anObject ...
MyObject copyObject = Copy(o, MyObject.class);
}