Почему перечисления Java не клонируются? - PullRequest
6 голосов
/ 26 ноября 2009

Слишком поздно менять вопрос, но точнее было бы спросить: «Почему clone () не допускает синглтоны?». copy() метод был бы более удобным.


Есть ли причина, по которой перечисления в Java не могут быть клонированы?

В руководстве указано, что

Это гарантирует, что перечисления никогда не клонируются, что необходимо для сохранения их «одноэлементного» статуса.

Но возврат самого экземпляра также сохранит его статус, и я смогу обрабатывать связанные перечисления так же, как и другие клонируемые объекты.

Можно утверждать, что

Общее намерение [clone ()] заключается в том, что для любого объекта x выражение: x.clone() != x будет правдой, [...]

Но для одиноких наоборот я хочу, чтобы x.clone() == x было правдой. Если сам экземпляр будет возвращен, то шаблон синглтона будет прозрачным для ссылок на объекты.

Так почему же нельзя перечислить перечисления или они забыли подумать о синглетах и ​​неизменяемых, когда было указано clone()?

Ответы [ 6 ]

8 голосов
/ 26 ноября 2009

Какова цель клонирования синглтона, если x.clone() == x? Разве вы не можете просто использовать x сразу.

Строго говоря, если вы хотите клонировать что-то и принудительно x.clone() == x, то единственным результатом, который может быть результатом клона, является x сам:

def clone() {
  return this;
}

Что может вводить в заблуждение ...


Если вы разрабатываете что-то и используете clone() для дифференциации, вы делаете это неправильно ИМХО ...

6 голосов
/ 26 ноября 2009

Если ваш метод клонирования возвращает this экземпляр, а не отдельный объект, тогда это не клон, не так ли?

Javadoc говорит:

По соглашению, объект возвращается этот метод должен быть независимым от этот объект (который клонируется).

Перечисления не должны быть клонированы, потому что предполагается, что когда-либо будет только один экземпляр каждого значения.

РЕДАКТИРОВАТЬ: В ответ на следующий комментарий:

Это именно то, что я критикую. Зачем не вернуть тот же экземпляр, если есть не может быть другим?

Потому что это не имеет смысла. Если это тот же объект, то это не клон. Javadocs также говорят:

Общее намерение таково, что для любого объект х, выражение:

x.clone() != x
будет правдой, и что выражение:
x.clone().getClass() == x.getClass()
будет правдой, но эти не являются абсолютными требованиями.

Таким образом, метод clone() предназначен для возврата отдельного объекта. К сожалению, это говорит о том, что это не абсолютное требование, которое делает ваше предложение действительным, но я все еще думаю, что это не имеет смысла, потому что бесполезно иметь метод клона, который возвращает this. Это может даже вызвать проблемы, если вы делаете что-то сомнительное, например, изменяющееся состояние в константах перечисления или синхронизацию с ними. Поведение такого кода будет различным в зависимости от того, правильно ли клонировал метод клонирования или только что вернул this.

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

5 голосов
/ 26 ноября 2009

Но для одиноких наоборот хочу, чтобы x.clone() == x было правдой.

Вы можете захотеть, но я думаю, что это странно, что следующий код сломается:

interface Foo extends Cloneable { public int getX(); public void setX(int x);  }
enum FooSingleton implements Foo { 
    INSTANCE; 
    private int x;
    public int getX(){ return x; }
    public void setX(int x){ this.x = x; }
}
class FooClass implements Foo { 
    private int x;
    public int getX(){ return x; }
    public void setX(int x){ this.x = x; }
}

boolean omg(Foo f){
    Foo c = f.clone();
    c.setX(c.getX() + 1);
    return c.getX() != f.getX();   
}
assert omg(new FooClass());        // OK
assert omg(FooSingleton.INSTANCE); // WTF?

(Конечно, поскольку clone() дает только полные копии, даже правильная реализация может привести к ошибкам в приведенном выше коде.)

С другой стороны, я могу согласиться с тем, что для операций клонирования было бы целесообразно просто return this для неизменяемых объектов, и перечисления действительно должны быть неизменяемыми. Теперь, когда был написан контракт на clone(), они, очевидно, не думали об неизменяемых объектах или не хотели особого случая для концепции, которая не поддерживается языком (то есть неизменяемых типов).

Итак, clone() - это то, что есть, и вы не можете очень хорошо пойти и изменить что-то, что существовало со времен Java 1.0. Я совершенно уверен, что где-то там есть код, который полностью полагается на clone(), возвращающий новый, отличный объект, возможно, в качестве ключа для IdentityHashMap или чего-то еще.

1 голос
/ 26 ноября 2009

Ваш собственный ответ на ваш вопрос самый лучший. В общем, люди ожидают, что clone() вернет другой объект. Семантика Cloneable сама по себе имеет больше смысла. («Объект является клонируемым ... о, я должен быть в состоянии сделать копии».) Я не могу придумать случайную ситуацию, где это имеет значение, но это было предполагаемое семантическое значение Cloneable.

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

1 голос
/ 26 ноября 2009

Полагаю, они не хотели рассматривать синглтоны как особый случай, когда было указано clone(). Это усложнило бы спецификацию. Так что теперь разработчикам библиотек приходится относиться к ним как к особому случаю, но всем остальным приятно, что мы можем доверять этому x.clone() != x.

0 голосов
/ 26 ноября 2009

Но для одиноких наоборот хочу, чтобы x.clone() == x было правдой.

Нет, это не был бы клон. Итак, для одиноких, вы хотите это:

public Object clone() throws CloneNotSupportedException {
  throw new CloneNotSupportedException(); 
}
...