Java: почему класс, предназначенный для наследования, должен содержать защищенный метод клонирования?

Я внимательно читаю «Эффективную Java» (Джошуа Блох) и нашел следующее предложение о клонировании:

Если вы разрабатываете класс для наследования, имейте в виду, что если вы решите не предоставлять защищенный метод клонирования с хорошим поведением, подклассы не смогут реализовать Cloneable.

Я немного запутался, потому что в моем маленьком тесте:

public class Cloning {

    public static void main(String[] args) throws CloneNotSupportedException {

        Woman woman1 = new Woman("Marie Curie-Sklodowska", 33, "Physics");
        Woman woman2 = (Woman) woman1.clone();
    }

    static abstract class Person {
        protected String name;
        protected int age;

        Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }

    static class Woman extends Person implements Cloneable {

        private String field;

        Woman(String name, int age, String field) {
            super(name, age);
            this.field = field;
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
}

Я не получаю никакой ошибки. Думаю, я не правильно понял фразу. Кто-нибудь может объяснить, что имел в виду автор?


person agienka    schedule 13.11.2017    source источник
comment
Переопределите Person.clone() и сделайте так, чтобы он явно выдавал CloneNotSupportedException. Тогда это не сработает.   -  person Andy Turner    schedule 14.11.2017
comment
@AndyTurner Это больше похоже на выбор плохого метода clone.   -  person shmosel    schedule 14.11.2017
comment
@AndyTurner Вопрос определенно кажется дубликатом, но я вообще не получаю ответа. Насколько я могу судить, clone() отлично работает с приватными полями в суперклассе.   -  person shmosel    schedule 14.11.2017
comment
Хм, читаю пост на эту же тему, но принятый ответ мне тоже не ясен. В нем говорится, что создание приватных полей «нарушает обещание», но я пробовал с приватными полями - и это не имело значения. Клонированный объект тот же. Так что мой вопрос может быть клоном, но оригинальный вопрос не содержит удовлетворительного ответа.   -  person agienka    schedule 14.11.2017
comment
@nibsa Принятый ответ неверен. Ни в одном из ответов нет ничего, что требовало бы чего-то большего, чем то, что делает Object.clone(). Я не знаю, о чем здесь говорит Блох, если только это не глубокое копирование.   -  person user207421    schedule 14.11.2017
comment
Я снова открываю вопрос, потому что не верю, что на дубликат был дан удовлетворительный ответ.   -  person shmosel    schedule 14.11.2017


Ответы (1)


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

Он ошибается. Они могут. Рассмотреть возможность:

public class A
{
    private int fieldA;
}

public class B extends A implements Cloneable
{
    public B clone() throws CloneNotSupportedException
    {
        return (B)super.clone();
    }
}

B.clone() вернет точную копию экземпляра B, для которого он был вызван, включая A.fieldA. Опровергнут контрпримером: QED

[Я знаю, что этот clone() на самом деле не должен быть объявлен для генерирования CloneNotSupportedException, но это упрощает демонстрацию.]

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

некоторые программисты просто никогда не переопределяют метод clone()

и

классы, предназначенные для наследования, не должны его реализовывать [Cloneable]

Что еще более любопытно, так это то, что первое предложение выше является последним предложением во всем разделе и полностью не подтверждается ни аргументами, ни примерами, если только весь раздел не предназначен для того, чтобы доказать это, чего он не делает. Весь раздел настолько запутан, что трудно разобрать, что именно утверждается. Клонирование не так сложно понять или реализовать, как он говорит. И это не важно. Я не использовал его в серьезном коде за 20 лет Java, а также в так называемых «конструкторах копирования».

person user207421    schedule 16.11.2017
comment
Я ожидал, что что-то не так с кратким изложением раздела о клонировании. Но я думаю, что он достиг своей цели - я достаточно обескуражен, чтобы использовать метод clone() и интерфейс Cloneable. Спасибо за ответ! - person agienka; 18.11.2017