Почему нет клона по умолчанию () в Cloneable в Java 8
Cloneable
в Java по своей сути сломан. В частности, моя самая большая проблема с интерфейсом заключается в том, что он ожидает поведение метода, которое не определяет сам метод. Поэтому, если пересечение через Cloneable
list вы должны использовать отражение для доступа к его определенному поведению. Однако в Java 8 у нас теперь есть методы по умолчанию, и теперь я спрашиваю, почему нет default clone()
метод Cloneable
.
я понимаю, почему интерфейсы не могут использовать методы объектов по умолчанию, однако, это было явное проектное решение, и поэтому могут быть сделаны исключения.
я представляю умоляюще Object.clone()
и изменение его внутреннего кода на что-то вроде:
if(this instanceof Cloneable) {
return ((Cloneable) this).clone();
}
else {
throw new CloneNotSupportedException();
}
и двигаться дальше, что делает магия clone()
сделайте свою вещь как метод по умолчанию в Cloneable
. Это на самом деле не исправить это clone()
все еще можно легко реализовать неправильно, но это еще одно обсуждение само по себе.
насколько я могу, это изменение будет полностью обратная совместимость:
- классы, которые в настоящее время переопределить
clone()
но не реализовалиCloneable
(почему?!) все равно было бы технически нормально (даже если функционально невозможно, но это ничем не отличается от того, что было раньше). - классы, которые в настоящее время переопределить
clone()
, но реализоватьCloneable
все равно будет функционировать одинаково при его реализации. - классы, которые в настоящее время не переопределяют
clone()
, но реализоватьCloneable
(почему?!) сейчас следовать спецификации, даже если это не полностью функционально правильно. - те, которые использовали отражение и ссылались на
Object.clone()
все равно функционально работает. -
super.clone()
все равно будет функционально таким же, даже если он ссылаетсяObject.clone()
.
не говоря уже о том, что это решит огромную проблему, которая Cloneable
есть. Хотя утомительно и все еще легко реализовать неправильно, это решит огромную объектно-ориентированную проблему с взаимодействие.
единственная проблема, которую я вижу, это те, которые реализуют Cloneable
не обязаны переопределить clone()
, но это ничем не отличается от того, что было раньше.
обсуждалось ли это внутренне, но так и не принесло результатов? Если так, то почему? Если по этой причине интерфейсы не могут использовать методы объектов по умолчанию, не имеет ли смысла сделать исключение в этом случае, поскольку все объекты наследуют Cloneable
ждем clone()
в любом случае?
2 ответов
Ваш вопрос несколько более широкий и дискуссионный, но я могу пролить некоторый свет на этот вопрос.
на Эффективная Java™, Джошуа блох дает довольно краткое изложение ситуации. Он открывает с немного истории позади Cloneable
Cloneable интерфейс был предназначен как интерфейс mixin для объектов к объявите, что они разрешают клонирование. К сожалению, он не служит этой цели. Его основной недостаток заключается в том, что это не клон метод, и метод клонирования объекта защищен. Вы не можете, не прибегая к отражению, вызвать метод clone на объекте только потому, что он реализует Cloneable.
и продолжает рассуждать
[Cloneable]определяет поведение реализации защищенного клона объекта: если класс реализует Cloneable, метод клонирования объекта возвращает копию объекта по полю... Это очень нетипичное использование интерфейсов, а не один быть примером для подражания. Обычно реализация интерфейса говорит о том, что класс может сделать для своих клиентов. В случае Cloneable он изменяет поведение защищенного метода на суперклассе.
и
Если реализация Cloneable интерфейса должна иметь какое-либо влияние на класс, класс и все его суперклассы должны подчиняться довольно сложным, невыполнимым, и плохо документированный протокол. Полученный механизм является экстралингвистическим: он создает объект без вызова конструктора.
есть много деталей, которые входят в это, но отметить только одну проблему:
архитектура клонирования несовместима с обычным использованием конечных полей, ссылающихся на изменяемые объекты.
Я думаю, что этого достаточно, чтобы рассуждать против наличия default
метод в интерфейсе делает клонирование. Было бы крайне сложно правильно его реализовать.
мой опыт, вероятно, далек от мейнстрима, но я использую clone()
и поддерживает текущий дизайн Cloneable
. Наверное, было бы лучше иметь его в качестве аннотации, но Cloneable
появился задолго до аннотаций. Мое мнение таково Cloneable
-это низкоуровневая вещь, и никто не должен делать что-то вроде obj instanceof Cloneable
. Если вы используете Cloneable
в некоторой бизнес-логике гораздо лучше объявить свой собственный интерфейс или абстрактный класс, который предоставляет clone()
для публикации и реализации это во всех ваших бизнес-логических объектах. Иногда вы, вероятно, захотите не выставлять clone()
на самом деле, но создать свой собственный метод, который использует clone()
внутренне.
например, учтите, что у вас есть иерархия именованных объектов, где имя не может быть изменено после построения, но вы хотите разрешить клонирование их с новым именем. Вы можете создать такой абстрактный класс:
public abstract class NamedObject implements Cloneable {
private String name;
protected NamedObject(String name) {
this.name = name;
}
public final String getName() {
return name;
}
public NamedObject clone(String newName) {
try {
NamedObject clone = (NamedObject)super.clone();
clone.name = newName;
return clone;
}
catch(CloneNotSupportedException ex) {
throw new AssertionError();
}
}
}
здесь, хотя вы реализуете Cloneable
, вы хотите использовать clone()
, но не хочу выставлять это на всеобщее обозрение. Вместо этого вы предоставляете другой метод, который позволяет клонировать с другим именем. Так имея public clone()
на Cloneable
излишне загрязняет открытый интерфейс ваших классов.
еще один случай, когда я использую Cloneable
реализация Spliterator.trySplit()
. Вижу реализация простой как spliterator, которая возвращает заданное число постоянных объектов. Он имеет четыре специализации (для объектов, ints, longs и Double), но благодаря clone()
Я могу реализовать trySplit()
только один раз в суперклассе. Опять же, я не хочу разоблачать clone()
, Я просто хочу использовать его сам.
Итак, чтобы заключить, не имея clone()
метод Cloneable
интерфейс на самом деле более гибкий, поскольку он позволяет мне решить, хочу ли я его публиковать или нет.