2015-12-06 6 views
10

Cloneable в Java по своей сути не работает. В частности, моя самая большая проблема с интерфейсом заключается в том, что он ожидает поведения метода, который не определяет сам метод. Поэтому, если вы переходите через список Cloneable, вы должны использовать отражение для доступа к его определенному поведению. Однако в Java 8 у нас теперь есть методы по умолчанию, и теперь я спрашиваю, почему в Cloneable нет метода clone() по умолчанию.Почему по умолчанию clone() в Cloneable в Java 8

Я понимаю, почему interfaces cannot default Object methods, однако это было явное дизайнерское решение, и поэтому могут быть сделаны исключения.

Я сортирую себе протестующий 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(), но это ничем не отличается от того, что было раньше.

Было ли это обсуждено внутренне, но так и не пришло в себя? Если да, то почему? Если это связано с тем, что интерфейсы не могут использовать методы Object по умолчанию, не имеет смысла делать исключение в этом случае, так как все объекты, наследующие Cloneable, ожидают clone() в любом случае?

+5

Я помню, где-то читал (из официальных источников), что «Cloneable» был настолько сломан, что даже не стоило исправлять. Но одна из проблем с вашим методом по умолчанию заключается в том, что использование 'this' здесь сложно. – Tunaki

+5

По сути, то, что говорит @Tunaki, является правильным. Невозможность возврата на сложность такого рода обручей не была; мы решили инвестировать наши бюджеты {время, усилие, сложность) в другие области, которые принесли большую ценность. –

+1

Нет смысла создавать такой метод 'default'. Если в 'java.lang.Object' и методе' default' есть один и тот же имя и подпись в «интерфейсе» есть метод, метод, объявленный в 'java.lang.Object', по-прежнему выигрывает за неквалифицированные вызовы. И если метод в java.lang.Object' остается «защищенным», все реализации «Cloneable» применяются для повторного использования метода как «public», а при изменении его на «public» все равно потребуется адаптировать существующие реализации. Другими словами, результат не отличается от определения метода 'abstract clone()' в интерфейсе 'Cloneable'. – Holger

ответ

6

Ваш вопрос несколько широк и более дискуссионный, но я могу пролить свет на этот вопрос.

Эффективный Java ™, Joshua Bloch дает довольно краткое описание ситуации.Он открывает немного истории позади Cloneable

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

и продолжает рассуждения

[Cloneable] определяет поведение защищенной реализации клона Объекта: если класс реализует Cloneable, метод клонирования объекта возвращает поле за полем копию объекта. Это очень атипичное использование интерфейсов, а не эмулирование. Обычно реализация интерфейса говорит о том, что класс может сделать для своих клиентов. В случае Cloneable он изменяет поведение защищенного метода на суперклассе.

и

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

Есть много деталей, которые входят в это, но отметить только одна проблема:

Архитектура клон несовместима с нормальным использованием конечных полей со ссылкой на изменяемые объекты.

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

5

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

Другой случай, когда я использую Cloneable, представляет собой реализацию Spliterator.trySplit(). См. implementation простого разделителя, который возвращает заданное количество постоянных объектов. Он имеет четыре специализации (для объектов, ints, longs и double), но благодаря clone() я могу реализовать trySplit() только один раз в суперклассе. Опять же, я не хочу показывать clone(), я просто хочу использовать его сам.

В заключение, не имея метода clone() в интерфейсе Cloneable, на самом деле он более гибкий, поскольку он позволяет мне решить, хочу ли я его публиковать или нет.

+0

@Holger, это другой метод, который принимает параметр в отличие от 'protected clone()' –