Почему нет клона по умолчанию () в 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 интерфейс на самом деле более гибкий, поскольку он позволяет мне решить, хочу ли я его публиковать или нет.