Чому Cloneable не застарілий?


139

Загальновідомо, що Cloneableінтерфейс у Java зламаний. Для цього є багато причин, про які я не згадую; інші це вже зробили. Це також позиція самих архітекторів Java .

Моє запитання полягає в тому: чому ще не було застаріло? Якщо основна команда Java вирішила, що вона порушена, вони, мабуть, також розглядають питання про втрату. Які їх причини проти цього (у Java 8 це ще не застаріло )?


41
Це питання не "в першу чергу засноване на думці", оскільки багато хто, мабуть, відчувають право судити. Ті, хто не має нічого, крім думки щодо причин, просто не мають права відповідати. Однак це правда, що ви маєте лише віддалений шанс отримати тут авторитетну відповідь. Також вірно, що ваше запитання не стосується вирішуваної проблеми у вас, тому це принаймні прикордонний поза темою.
Марко Тополник

6
@MarkoTopolnik Я погоджуюся, що в світі є люди, які могли б дати авторитетну відповідь, але я не вірю, що це тест, який ми застосовуємо тут. Причина закриття стверджує, що "відповіді на це питання , як правило, майже повністю базуються на думках". Я підозрюю, що так буде і тут, якщо нам не пощастить.
Дункан Джонс

2
Ось "Як і коли" застаріти з Oracle ... ( docs.oracle.com/javase/6/docs/technotes/guides/javadoc/… ) Інтерфейс, що може бути закритий, може потрапити у випадку "баггі чи дуже неефективного", але це дуже відкрито до думок.
Макс

8
@Duncan Я все ще не вважаю справедливим приймати рішення з питання, виходячи з моїх припущень щодо недостатньої дисципліни з боку відповідачів . Якщо користувач не знає причини, про яку вас запитують, він не має права зловживати відповідним центром, щоб викласти свою думку з цього питання.
Марко Топольник

4
@lexicore Так, саме --- і можна поспорити, що вони ретельно розглядали цей варіант і, маючи на увазі, повинні мати вагомі причини, щоб не знехтувати його. Їх власна критика Cloneableшироко відома.
Марко Топольник

Відповіді:


120

Існує помилка, подана в 1997 році в базу даних Java Bug про додавання clone()методу до Cloneable, тому це більше не буде марним. Він був закритий резолюцією "не виправить", а обґрунтування було наступним:

Комітет з технічного огляду Sun (TRC) детально розглядав це питання і рекомендував не вживати жодних заходів, окрім удосконалення документації поточного інтерфейсу Cloneable . Ось повний текст рекомендації:

Існуючі API клонування об’єктів Java є проблематичними. Існує захищений метод "клонування" на java.lang.Object і є інтерфейс java.lang.Cloneable. Намір полягає в тому, що якщо клас хоче дозволити іншим людям клонувати його, то він повинен підтримувати інтерфейс Cloneable і заміняти метод клонування, захищений за замовчуванням, методом public clone. На жаль, з причин, зручно загублених у смугах часу, інтерфейс Cloneable не визначає метод клонування.

Це поєднання призводить до неабиякої сум'яття. Деякі класи заявляють, що підтримують Cloneable, але випадково забувають підтримувати метод клонування. Розробники плутаються в тому, як повинен працювати Cloneable і що клон повинен робити.

На жаль, додавання методу "клонування" до Cloneable було б несумісною зміною. Він не порушить бінарну сумісність, але порушить сумісність із джерелами. Анекдотичні дані свідчать про те, що на практиці існує ряд випадків, коли класи підтримують інтерфейс Cloneable, але не забезпечують публічний метод клонування. Після обговорення TRC одноголосно рекомендував НЕ змінювати існуючий інтерфейс Cloneable через вплив на сумісність.

Альтернативна пропозиція полягала в тому, щоб додати новий інтерфейс java.lang.PubliclyCloneable, щоб відобразити початкову мету Cloneable. Більшою кількістю 5 - 2 TRC рекомендував проти цього. Основна стурбованість полягала в тому, що це додасть ще більше плутанини (включаючи орфографічну плутанину!) До вже заплутаної картини.

TRC одноголосно рекомендував нам додавати додаткову документацію до існуючого інтерфейсу Cloneable, щоб краще описати, яким чином він планується використовувати, та описати "кращі практики" для виконавців.

Тож, хоча це не стосується безпосередньо застарілого , причиною не зробити Cloneable «застарілою» є те, що Комітет з технічного огляду вирішив, що зміна існуючої документації буде достатньою, щоб зробити цей інтерфейс корисним. І так вони зробили. До Java 1.4 Cloneableбуло задокументовано наступне:

Клас реалізує інтерфейс Cloneable, щоб вказати методу Object.clone (), що законно для цього методу зробити копію «поле для поля» екземплярів цього класу.

Спроби клонувати екземпляри, які не реалізують інтерфейс Cloneable, призводять до того, що виняток CloneNotSupportedException буде викинуто.

Інтерфейс Cloneable не оголошує методів.

З Java 1.4 (яка вийшла в лютому 2002 року) і до поточної версії (Java 8) це виглядає приблизно так:

Клас реалізує інтерфейс Cloneable, щоб вказати методу Object.clone (), що законно для цього методу зробити копію «поле для поля» екземплярів цього класу. Викликаючи метод клонування Object на екземпляр, який не реалізує інтерфейс Cloneable, призводить до викидання винятку CloneNotSupportedException.

За умовою, класи, які реалізують цей інтерфейс, повинні перекривати Object.clone (який захищений) з відкритим методом. Детальнішу інформацію про перегляд цього методу див. У Object.clone ().

Зауважте, що цей інтерфейс не містить методу клонування. Тому клонувати об’єкт неможливо лише завдяки тому, що він реалізує цей інтерфейс. Навіть якщо метод клонування викликається рефлекторно, немає гарантії, що він матиме успіх.


3
а чи знаєте ви, чому cloneметод був у Objectпершу чергу?
njzk2

8
@ njzk2 Це важлива частина механізму - це метод виконувати низькорівневу, екстралінгвістичну магію копіювання біта зображення об'єкта на біт.
Марко Тополник

3
@Unheilig Ця магія - це те, що ви не можете повторити за допомогою конструктора копій: Object#clone()створює екземпляр того ж класу, що і оригінал, без того, щоб цей клас був відомий під час компіляції.
Марко Топольник

4
@AVolpe: Це не виправить несумісність джерела, згадану у відповіді "там, де класи підтримують інтерфейс Cloneable, але не забезпечують публічний метод клонування". Зокрема, класи, що надають непублічний метод клонування, будуть порушені.
Луї Вассерман

2
Приємна історія. Ось пряме посилання на базу даних про помилки. Я додав ще трохи історії, і я цитував її частини у своїй відповіді .
Стюарт Маркс

64

Коротка відповідь на "чому не Cloneableзастаріла?" (і справді, чому ні Xдля кого не є застарілим X) - це те, що їх приниження не приділялося багато уваги.

Більшість речей, які були застаріли останнім часом, були застарілими, оскільки існує конкретний план їх видалення. Наприклад, addPropertyChangeListenerі removePropertyChangeListenerметоди LogManager були застаріли в Java SE 8 з наміром видалити їх у Java SE 9. (Причина в тому, що вони надмірно ускладнюють взаємозалежності модулів.) Дійсно, ці API вже видалено з ранньої розробки JDK 9 будує. (Зверніть увагу, що аналогічні виклики слухачів зміни властивостей також були видалені з Pack200; див. JDK-8029806 .)

Не існує такого подібного плану для Cloneableта Object.clone().

Більш довга відповідь передбачає обговорення подальших запитань, наприклад, що можна очікувати, що трапиться з цими API, які витрати або вигоди буде нараховувати платформу, якщо вони були застарілими, і що повідомляється розробникам, коли застарілий API. Я досліджував цю тему в своїх останніх розмовах JavaOne, " Заборгованість" та "Позбавлення життя" . (Слайди доступні за цим посиланням; відео тут .) Виявляється, сам JDK не був дуже послідовним у використанні депресії. Він використовувався для позначення кількох різних речей, зокрема, наприклад,

  • Це небезпечно , і ви повинні бути інформовані про ризики , пов'язаних з використанням (приклад: Thread.stop(), Thread.resume(), і Thread.suspend()).

  • Це буде видалено в майбутньому випуску

  • Це застаріло, і вам корисно використовувати щось інше (приклад: багато методів в java.util.Date)

Все це є різними значеннями, і різні підмножини їх застосовуються до різних речей, які застаріли. І деякі підмножини з них застосовуються до речей, які не є застарілими (але, можливо, повинні бути застарілими).

Cloneableі Object.clone()"розбиті" в тому сенсі, що вони мають вади дизайну і їх важко правильно використовувати. Однак, clone()як і раніше кращий спосіб копіювання масивів, і клонування має деяку обмеженість корисності для копіювання екземплярів класів, які ретельно реалізуються. Видалення клонування було б несумісною зміною, яка б порушила багато речей. Операція з клонування може бути повторно реалізована по-іншому, але це, мабуть, буде повільніше, ніж Object.clone().

Однак для більшості речей конструктор копій кращий для клонування. Тому, можливо, маркування Cloneableяк "застаріле" або "замінене" чи щось подібне було б доречним. Це скаже розробникам, що вони, ймовірно, хочуть шукати деінде, але це не означатиме, що механізм клонування може бути видалений у майбутньому випуску. На жаль, такого маркера не існує.

За станом на даний момент, здається, що «депресія» передбачає можливе усунення - незважаючи на те, що ніколи не було вилучено малу кількість застарілих функцій - і таким чином, припинення роботи не здається гарантованим для механізму клонування. Можливо, в майбутньому може бути застосоване альтернативне маркування, яке спрямовує розробників використовувати альтернативні механізми замість цього.

ОНОВЛЕННЯ

До звіту про помилку я додав додаткову історію . Френк Єллін, ранній виконавець проекту JVM та співавтор специфікації JVM, зробив деякі коментарі у відповідь на коментар "втрачений у туманах часу" коментар до рекомендації TRC, цитованої в іншій відповіді . Я цитував відповідні частини тут; повне повідомлення знаходиться у звіті про помилку.

У Cloneable немає методів з тієї ж причини, що і у Serializable. Cloneable вказує на властивість класу, а не конкретно говорити про методи, які підтримує клас.

До роздумів нам знадобився нативний метод, щоб зробити дрібну копію Об’єкта. Звідси народився Object.clone (). Також було зрозуміло, що багато класів хочуть перекрити цей метод, і що не кожен клас хотів би бути клонованим. Отже, Cloneable народився, щоб вказати на наміри програміста.

Отже, коротше. Метою Cloneable не було вказувати на те, що у вас відкритий метод clone (). Це означало, що ви хочете клонуватись за допомогою Object.clone (), і саме від реалізації вирішувати, чи робити чи клонування () публічним клоном (), слід було.


3
Один прекрасний відповідь у вас тут, сер. Мені особливо подобається, що ти не просто кидаєш Object.clone()у вогонь тільки тому, що всі інші хочуть, але ти готовий міркувати і виховувати в цьому добрі речі.
icza

2
Однак clone () все ще є найкращим способом копіювання масивів, а клонування має обмежену корисність для копіювання екземплярів класів, які ретельно реалізуються. Я опинився під враженням від виправлення 6428387, усі шляхи коду (клон, new / arrayCopy, Arrays.copyOf) призвели до однакових властивостей. Чи нещодавно змінилося?
bestsss

2
@bestsss Я не думаю, що array.clone()це обов'язково швидше будь-яких альтернатив. З точки зору API це найбільш стислий спосіб дублювання масиву. Arrays.copyOf(array, newlen)наближається, але йому потрібен параметр довжини, який є зайвим, якщо ви не змінюєте довжину.
Стюарт Маркс

2
@Holger Так, наскільки ми бачимо, це перше фактичне видалення API з 1.1. Зауважте також, що хоча ми погоджуємось, що Thread.suspend()і Thread.stop()(no-arg) небезпечні, вони, ймовірно, не будуть видалені - або змінені, щоб викинути виняток беззастережно - тому що люди насправді ними користуються! Імовірно, вони готові нести ризик. Одним із пом'якшуючих факторів слухачів змін властивостей є те, що вони використовувалися дуже рідко, тому вплив їх видалення невеликий.
Стюарт Маркс

2
@Holger Концептуально java.beansможна зробити незалежним, java.desktopоскільки боби - це лише бібліотечний API для властивостей. На жаль, якщо ви копаєтесь в API API бобів, існує велика кількість залежностей від AWT. Впровадження є ще більше. Звичайно, можливо, їх можна вилучити, але це виглядає як набагато більше роботи, ніж, скажімо, відмежування лісозаготівлі від бобів. Цілі зусилля з модуляризації полягають у виконанні цього роз'єднання; безсумнівно, можна було б зробити і більше, але тоді Мотоблок зайняв би ще більше часу.
Стюарт Маркс

-1

чому це ще не застаріло?

Тому що JCP не вважає за потрібне це робити, і може ніколи цього не робити. Запитайте їх. Ви запитуєте в неправильному місці.

Які причини зберігати цю річ у Java API

Ніхто ніколи не зніме нічого з Java API через вимогу зворотної сумісності. Востаннє, що сталося, було зміна моделі подій AWT між 1,0 та 1,1 у 1996/7.


17
Вони (ефективно) видалили Thread.stop(Throwable), змінивши його договір, щоб завжди кидати UnsupportedOperationExceptionна абонента (а не на цільову нитку!).
Марко Тополник

22
Що відбулося приблизно в той же час? Видалення Thread.stop(Throwable)функціональних можливостей трапилося в Java 8. Як би то не було, кваліфікована порада "запитати їх" неправильна, оскільки сьогодні сам головний архітектор Java є активним учасником Stack Overflow. Він просто не турбується відповідати ні на що, окрім питань, пов’язаних із потоками.
Марко Тополник

13
Окрім того, питання ОП не стосується усунення , а депресії , і явно старіння відбувається протягом усього часу.
Марко Тополник

17
@EJP Я не запитую, чи Cloneableбуде видалено Java API. Я запитую, чому це не буде знехтувати. І я думаю, що це ідеальне місце для запитань.
Као

5
@VaheHarutyunyan Дякую за вигук, але я не архітектор Java. Я інженер групи JDK Oracle, яка підтримує цей матеріал.
Стюарт Маркс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.