Почему Java Cloneable Interface не является общим?

В Java 5 были введены обобщения, и они были добавлены ко многим интерфейсам в пакете java.lang. Однако Cloneable не получал дженерики. Интересно, почему?


Изменить: В ответ на ответы @Jon и @litb и комментарий @Earwicker я думал, что Cloneable может быть:

public interface Cloneable<T> {
    public T clone();
}

Здесь T clone(); переопределяет Object.clone(), давая ему ковариантный тип. Я считаю, что это все равно будет обратно совместимым и повысит безопасность типов. Так почему бы и нет?


Изменить 2: Как видно из ответов (и комментариев) ниже, предлагаемый выше интерфейс нарушит совместимость с обратной связью. Поскольку Object.clone() является protected, переписывание его в интерфейсе заставит всех разработчиков реализовать реализацию public, которую разработчики классов могут не захотеть (т.е. Они могут оставить его protected).

Ответ 1

Интерфейс Cloneable не содержит каких-либо элементов. Какой смысл сделать его общим?

(Если Cloneable содержит метод clone(), это имеет смысл - но это объявлено в java.lang.Object.)

EDIT: clone() находится в java.lang.Object, так как он имеет реализацию (которая выполняет полевую копию). Лучшим вариантом было бы иметь что-то вроде .NET MemberwiseClone() как защищенный метод в объекте, а затем общедоступный метод clone() в пределах самого интерфейса. Я не знаю, почему этот дизайн не был выбран.

(В .NET, ICloneable не является общим, поскольку он существовал до дженериков - различная природа .NET-дженериков предотвращает ранее не общий тип от родового.)

Однако в Java и .NET "нормальный" API клонирования обычно считается плохим, поскольку он ничего не говорит о том, какую глубину клонирования следует выполнять.

Ответ 2

Если Cloneable было Cloneable<T>, тогда было бы невозможно сделать это:

class A extends B implements Cloneable<A>

class B implements Cloneable<B>

Вы не можете реализовать один и тот же интерфейс с разными параметрами, а класс A реализует Cloneable<A> и Cloneable<B>.

Ответ 3

java.lang.Cloneable - интерфейс маркера. Это исключительно для этой цели, так что Object.clone может генерировать исключение для сигнала, который класс не поддерживает клонирование, используя, например,

if(!(this instanceof Cloneable))
    throw...;

Из документации:

Метод clone для класса Object выполняет определенную операцию клонирования. Во-первых, если класс этого объекта не реализует интерфейс Клонируемый, затем Вызывается CloneNotSupportedException.

У него нет методов. Сделать его универсальным не будет.

Ответ 4

Это не касается вашего теоретического вопроса, но обрабатывает практический случай:

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

public class MyClass implements Cloneable {
    public MyClass clone(){ /* do the right stuff */ }
}

MyClass.clone() является правильным переопределением Object.clone(), и вам не придется писать касты.

Ответ 5

Как вопрос, связанный с полусвязью: создаст ли интерфейс (назовите его ReallyCloneable), который предоставляет clone() как полезный публичный член?

Я утверждаю, что нет, это не так. Клонируемость тесно связана с реализацией конкретного класса. Я не могу придумать ни одного случая использования, где я могу сказать "у меня есть произвольный объект, и я хочу его копию". Непосредственный вопрос: почему вы хотите эту копию?

И типичный ответ заключается в том, что вы можете изменить копию, не затрагивая оригинал. Однако для этого вам нужно знать, какой тип объекта вы держите, иначе как вы узнаете, что вызвать, чтобы его изменить? И если вы это знаете, вы узнаете, предоставляет ли он публичный метод clone().

Но что, если вы программируете на интерфейс, например List? Интеллектуальное (рекурсивное) клонирование (по сравнению с копией данных объекта на уровне байта) было бы невероятно полезным в рамках коллекции. Но могут быть коллекции (например, те, которые поддерживаются базой данных), которые не могут поддерживать такую ​​операцию, поэтому вы не можете требовать, чтобы List открывал публичный clone(). Это побуждает вас создавать экземпляр собственной реализации списка List для копирования содержимого исходного списка.