Почему #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;
}