Мне интересно, если есть рекомендуемый способ сделать глубокий клон / копию экземпляра в Java.
Я имею в виду 3 решения, но я могу пропустить некоторые, и я хотел бы узнать ваше мнение
отредактируйте: включите предложение Bohzo и уточните вопрос: это больше о глубоком клонировании, чем мелком клонировании.
Сделай сам:
закодируйте свойства клона вручную после свойств и убедитесь, что изменяемые экземпляры тоже клонированы.
pro:
- контроль над тем, что будет выполнено
-
минусы быстрого выполнения :
- утомительно писать и поддерживать
- склонны к ошибкам (ошибка копирования / вставки, отсутствующее свойство, переназначенное изменяемое свойство)
Используйте отражение:
С помощью ваших собственных инструментов отражения или с помощью внешнего помощника (например, jakarta common-bean) легко написать общий метод копирования, который будет выполнять работу в одну строку.
Pro:
- Легко писать
- Нет
минусов в обслуживании :
- Меньше контроля над тем, что происходит
- Ошибка подвержена изменчивому объекту, если инструмент отражения тоже не клонирует дочерние объекты
- Более медленное выполнение
Используйте клон рамки:
Используйте платформу, которая сделает это за вас, например:
commons-lang SerializationUtils
Библиотека глубокого клонирования Java
Dozer
Kryo
за:
- так же, как отражение
- больше контроля над тем, что именно будет клонировано.
минусы:
- каждый изменяемый экземпляр полностью клонируется, даже в конце иерархии
- может выполняться очень медленно
Используйте инструментарий байт-кода для записи клона во время выполнения
javassit , BCEL или cglib могут быть использованы для генерации выделенного клонера так же быстро, как написана одна рука. Кто-то знает библиотеку, использующую один из этих инструментов для этой цели?
Что я здесь упустил?
Какой из них вы бы порекомендовали?
Спасибо.
Ответы:
Для глубокого клонирования (клонирует всю иерархию объектов):
commons-lang SerializationUtils - используя сериализацию - если все классы находятся под вашим контролем, и вы можете принудительно реализовать
Serializable
.Java Deep Cloning Library - использование отражения - в случаях, когда классы или объекты, которые вы хотите клонировать, находятся вне вашего контроля (сторонняя библиотека) и вы не можете заставить их реализовать
Serializable
, или в случаях, когда вы не хотите реализовыватьSerializable
,Для мелкого клонирования (клонирует только свойства первого уровня):
commons-beanutils BeanUtils - в большинстве случаев.
Spring BeanUtils - если вы уже используете spring и, следовательно, имеете эту утилиту на пути к классам.
Я намеренно опустил опцию «сделай сам» - вышеприведенные API обеспечивают хороший контроль над тем, что клонировать, а что нет (например, с помощью
transient
илиString[] ignoreProperties
), поэтому повторное создание колеса не является предпочтительным.источник
В книге Джошуа Блоха есть целая глава, озаглавленная «Пункт 10: Правильно отвергнуть клона» в которой он объясняет, почему переопределение клона по большей части является плохой идеей, потому что спецификация Java для него создает много проблем.
Он предлагает несколько альтернатив:
Используйте шаблон фабрики вместо конструктора:
Используйте конструктор копирования:
Все классы коллекции в Java поддерживают конструктор копирования (например, new ArrayList (l);)
источник
Copyable
интерфейс, который содержитgetCopy()
метод. Просто используйте шаблон прототипа вручную.newInstance()
метод иYum
конструктор будут делать глубокое или поверхностное копирование?Начиная с версии 2.07 Kryo поддерживает мелкое / глубокое клонирование :
Kryo быстр, на их странице вы можете найти список компаний, которые используют его в производстве.
источник
Используйте XStream toXML / fromXML в памяти. Чрезвычайно быстро и уже давно и набирает силу. Объекты не должны быть сериализуемыми, и вы не должны использовать отражение (хотя XStream делает). XStream может различать переменные, которые указывают на один и тот же объект, и не случайно делает две полные копии экземпляра. Много таких деталей вырабатывалось годами. Я использовал это в течение многих лет, и это начало. Это так же просто, как вы можете себе представить.
или
Клонировать,
Более кратко:
источник
Для сложных объектов и когда производительность незначительна, я использую gson для сериализации объекта в текст json, а затем десериализации текста для получения нового объекта.
gson, основанный на отражении, будет работать в большинстве случаев, за исключением того, что
transient
поля не будут скопированы и объекты с круговой ссылкой с причинойStackOverflowError
.источник
Смотря как.
Для скорости используйте DIY. Для пуленепробиваемости используйте отражение.
Кстати, сериализация не совпадает с refl, так как некоторые объекты могут предоставлять переопределенные методы сериализации (readObject / writeObject), и они могут содержать ошибки
источник
Я бы порекомендовал способ DIY, который в сочетании с хорошими методами hashCode () и equals () должен легко проверяться в модульном тесте.
источник
Я бы предложил переопределить Object.clone (), сначала вызвать super.clone (), а затем вызвать ref = ref.clone () для всех ссылок, которые вы хотите глубоко скопировать. Это более или менее Сделай сам подход, но нужно немного меньше кодирования.
источник
Для глубокого клонирования внедрите Serializable для каждого класса, который вы хотите клонировать следующим образом.
И затем используйте эту функцию:
как это:
Obj newObject = (Obj)deepClone(oldObject);
источник