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

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

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

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

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

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

7 ответов

Решение

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

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

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

Сказать, ArrayList реализует clone (), который возвращает объект ArrayList,

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

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

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

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

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

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

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

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

Он не определяет, будет ли разработчик делать глубокую или поверхностную копию.

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

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

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

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

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

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

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

Другие вопросы по тегам