Подтвердить что ты не робот

Почему люди так боятся использовать clone() (на коллекции и классы JDK)?

Несколько раз я утверждал, что использование clone() не является такой плохой практикой. Да, я знаю аргументы. Блох сказал, что это плохо. Он действительно это сделал, но он сказал, что реализация clone() плоха. Использование клона с другой стороны, особенно если оно правильно реализовано доверенной библиотекой, такой как JDK, в порядке.

Вчера я обсуждал мой ответ, который просто предполагает, что использование clone() для ArrayList в порядке (и по этой причине у него не было процентов, Я думаю).

Если мы посмотрим на @author of ArrayList, мы увидим знакомое имя - Джош Блох. Таким образом, clone() в ArrayList (и других коллекциях) отлично подходит (просто посмотрите на их реализации).

То же самое относится к Calendar и, возможно, к большинству классов java.lang и java.util.

Итак, дайте мне повод, чтобы не использовать clone() с классами JDK?

4b9b3361

Ответ 1

Итак, дайте мне повод, чтобы не использовать clone() с классами JDK?

  • Для ссылки ArrayList вам понадобится проверка getClass, чтобы проверить, что она не является подклассом класса JDK. И что потом? Потенциальным подклассам нельзя доверять. Предположительно, какой-то подкласс будет иметь другое поведение.
  • Для этого требуется, чтобы ссылка была более конкретной, чем List. Некоторые люди не против, но мнение большинства заключается в том, что это плохая идея.
  • Вам придется иметь дело с актерским составом, небезопасным актером.

Ответ 2

Из моего опыта проблема clone() возникает на производных классах.

Скажем, ArrayList реализует клон(), который возвращает объект ArrayList.

Предположим, что ArrayList имеет производный класс, а именно MyArrayList. Это будет катастрофой, если MyArrayList не переопределяет метод clone(). (По умолчанию он наследует код от ArrayList).

Пользователь MyArrayList может ожидать, что clone() вернет объект MyArrayList; однако это не так.

Это раздражает: если базовый класс реализует clone(), его производный класс должен полностью переопределить clone().

Ответ 3

Я отвечу цитатой от самого человека (Джош Блох по дизайну - Копировать конструктор против клонирования):

Есть очень немного вещей, для которых я больше использую Cloneable. Я часто предоставляю метод public clone для конкретных классов, потому что люди ожидают его.

Он не может быть более явным, чем это: clone() в его классах Framework Framework предоставляется, потому что люди ожидают его. Если люди перестанут это ожидать, он бы с радостью выбросил его. Один из способов заставить людей перестать рассчитывать на то, чтобы научить людей прекращать использовать его, а не защищать его использование.

Конечно, сам Блох также сказал (не точную цитату, но близко): "API подобен сексу: сделайте одну ошибку, и вы поддерживаете ее на всю жизнь". Любые public clone(), вероятно, никогда не будут возвращены. Тем не менее, это не очень хорошая причина для его использования.

Ответ 4

Чтобы не поощрять других, менее опытных разработчиков к реализации Clone() самих себя. Я работал со многими разработчиками, чьи стили кодирования в значительной степени копируются из (иногда ужасного) кода, с которым они работали.

Ответ 5

Это не гарантирует, что разработчик выполнит глубокую или мелкую копию.

Ответ 6

Использование клона может быть рискованным очень рискованным, если вы не проверяете реализацию этого метода клонирования... поскольку вы можете предположить, что clone impl может действовать по-разному... мелким или глубоким клоном... и некоторые разработчики может не всегда проверять, какой клон они будут получать...

В большом приложении, большой команде, это также рискованно, потому что, когда клонирование используется, если вы изменяете реализацию клона, вы можете изменять поведение приложения и создавать новые ошибки... и должны проверять везде, где был вызван клон (но он может быть одинаковым для других объектных методов, таких как equals, toString...

При модификации клона небольшого подкласса A (от примера к примеру от Deep to Shallow clone), если экземпляр B имеет ссылку на A и глубокий clone impl, тогда, поскольку объекты, на которые ссылаются в A, не клонированы, клон B больше не будет глубоким клоном (и он тот же для любого класса, ссылающегося на экземпляр B...). Нелегко справиться с глубиной ваших методов клонирования.

Также, когда у вас есть интерфейс (расширение Clonable) и множество (много!) реализаций, иногда вы будете проверять некоторый имп и видеть, что клоны глубоки и вызывают клон на inteface, но вы не можете быть уверены во время выполнения все impl действительно имеют глубокий клон и могут вводить ошибки...

Подумайте, что лучше всего использовать для каждого метода метод мелкого clone и deepClone, а по очень специфическим потребностям - применять индивидуальные методы (например, вы хотите клонировать и ограничивать глубину этого клона до 2, создавать пользовательский имп для что во всех классах).

Не считайте важным использовать метод clone для класса JDK, поскольку он не будет изменен другим разработчиком или, по крайней мере, не часто. Но лучше не называть клон на классах JDK, если вы не знаете реализацию класса во время компиляции. Я имею в виду, что клон на ArrayList не является вопросом, но вызов его в сборнике может быть опасным, поскольку другая реализация Collection может быть введена другим разработчиком (он может даже расширять коллекцию impl), и ничто не говорит вам, что клон этого будет работать так, как вы ожидаете, что это произойдет...

Ответ 7

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