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

насколько я могу, это изменение будет полностью обратная совместимость:

  1. классы, которые в настоящее время переопределить clone() но не реализовали Cloneable (почему?!) все равно было бы технически нормально (даже если функционально невозможно, но это ничем не отличается от того, что было раньше).
  2. классы, которые в настоящее время переопределить clone(), но реализовать Cloneable все равно будет функционировать одинаково при его реализации.
  3. классы, которые в настоящее время не переопределяют clone(), но реализовать Cloneable (почему?!) сейчас следовать спецификации, даже если это не полностью функционально правильно.
  4. те, которые использовали отражение и ссылались на Object.clone() все равно функционально работает.
  5. 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 интерфейс на самом деле более гибкий, поскольку он позволяет мне решить, хочу ли я его публиковать или нет.