Почему #clone() не находится в интерфейсе Cloneable?
Я читал, когда выполнял глубокую копию массива правильно, однако я был смущен тем, как реализуется #clone()
. Он является членом класса java.lang.Object
, и все же, если вы читаете javadocs:
Во-первых, если класс этого объекта не реализует интерфейс Cloneable, генерируется исключение CloneNotSupportedException.
Так зачем определять метод clone
там, в первую очередь? Конечно, если метод можно использовать только при наличии интерфейса, вы должны поместить этот метод в интерфейс. Сам интерфейс Cloneable
пуст; это просто интерфейс маркера, используемый Java, чтобы гарантировать, что использование метода clone
является законным.
Выполнение этого способа также устраняет возможность использования дженериков для обеспечения безопасности типов:
class Foo implements Cloneable { // Valid.
@Override
public Object clone() throws CloneNotSupportedException {
// ...
}
}
class TypeSafeFoo implements Cloneable<TypeSafeFoo> { // Not valid.
@Override
public TypeSafeFoo clone() throws CloneNotSupportedException {
// ...
}
}
Почему Java сделала это так? Я уверен, что у них есть законные причины, но я не могу понять это.
Ответы
Ответ 1
Контракт клонирования в Java требует, чтобы каждая реализация clone
должна сначала получить клонированный экземпляр из super.clone()
. Это создает цепочку, которая всегда заканчивается вызовом Object.clone
, и этот метод содержит "магический" код нативного уровня, который делает двоичную копию базового raw struct
, который представляет объект Java. Если этого механизма не существует, clone
не будет полиморфным: метод Object.clone
создает экземпляр любого класса, на который он вызван; это невозможно воспроизвести без собственного кода.
Вот почему метод Object.clone
нельзя было избежать. Cloneable
мог содержать метод clone
, но это создавало бы проблемы в отношении предложения throws
. Способ, которым он стоит, свободен объявлять clone
без объявленных исключений или объявлять произвольные исключения. Эта гибкость будет невозможна, если этот метод уже был объявлен в интерфейсе.
Имейте в виду, что Generics мало полезны для клонирования: представьте protected T clone()
в Object
: откуда бы взялся T
? Нужно ли нам Object<T>
и заставить каждый класс в юниверсе Java параметрироваться сам по себе, и все это просто для того, чтобы этот полунепрерывный механизм работал немного лучше? Имейте в виду, что этот код совершенно легален:
public class TheMightyOne implements Cloneable {
@Override public TheMightyOne clone() {
return (TheMightyOne) super.clone();
}
}
Вы можете назвать это:
TheMightyOne one = new TheMightyOne();
TheMightyOne two = one.clone(); // do downcasts needed
Ответ 2
Чтобы справиться с созданием клонирования и базового копирования поля, клону необходимо наследовать реализацию метода. Класс Cloneable может появляться в любом месте иерархии и может потребовать расширения определенного суперкласса для выполнения основной работы. Единственным суперклассом, из которого все возможные классы Cloneable могут наследовать реализацию, является Object.
Клонирование было определено задолго до дженериков.
Ответ 3
clone()
не нужен, каждый объект может вызывать метод Object.clone
путем отражения, поэтому клонирование объекта зависит от того, следует ли реализовать интерфейс Cloneable
. Это по соображениям безопасности. Вы можете просто клонировать объект, который реализует Cloneable
с помощью этого использования:
@SuppressWarnings("unchecked")
public static <T extends Cloneable> T clone(Cloneable obj) {
T rtn = null;
try {
Method method = Object.class.getDeclaredMethod("clone");
method.setAccessible(true);
rtn = (T) method.invoke(obj);
} catch (Throwable t) {
t.printStackTrace();
}
return rtn;
}