it-roy-ru.com

Как клонировать абстрактные объекты с конечными полями в Java?

В вопросе this и этом post объясняется, как клонировать объекты с конечными полями с помощью конструкторов защищенных копий.

Однако предположим, что мы имеем:

public abstract class Person implements Cloneable
{
    private final Brain brain; // brain is final since I do not want 
                // any transplant on it once created!
    private int age;
    public Person(Brain aBrain, int theAge)
    {
        brain = aBrain; 
        age = theAge;
    }
    protected Person(Person another)
    {
        Brain refBrain = null;
        try
        {
            refBrain = (Brain) another.brain.clone();
            // You can set the brain in the constructor
        }
        catch(CloneNotSupportedException e) {}
        brain = refBrain;
        age = another.age;
    }
    public String toString()
    {
        return "This is person with " + brain;
        // Not meant to sound rude as it reads!
    }
    public Object clone()
    {
        return new Person(this);
    }

    public abstract void Think(); //!!!!
    …
}

Возвращает ошибку, так как мы не можем создать экземпляр абстрактного класса. Как мы можем решить это?

15
justHelloWorld

Вы не реализуете метод clone() в абстрактном классе, только в конкретных подклассах.

public class SomeConcretePerson extends Person
{
    public SomeConcretePerson (SomeConcretePerson another)
    {
        super (another); // this will invoke Person's copy constructor
    }

    public Object clone()
    {
        return new SomeConcretePerson(this);
    }
}
19
Eran

В некоторых редких случаях мы не можем использовать метод конструктора копирования и вынуждены использовать метод clone(). В этих случаях стоит знать, что Java предлагает обходной путь для проблемы поля final:

public abstract class Person implements Cloneable {
    private final Brain brain;
    private int age;
    public Person(Brain aBrain, int theAge) {
        brain = aBrain; 
        age = theAge;
    }
    @Override public String toString() {
        return "This is person with " + brain;
        // Not meant to sound rude as it reads!
    }
    @Override public Person clone() {
        try {
            Person clone = (Person)super.clone();
            Field brainField=Person.class.getDeclaredField("brain");
            brainField.setAccessible(true);
            brainField.set(clone, brain.clone());
            return clone;
        } catch (CloneNotSupportedException|ReflectiveOperationException ex) {
            throw new AssertionError(ex);
        }
    }

    public abstract void think();

    …
}

Возможность переопределить ограничение final была создана именно для таких случаев использования, клонирования или десериализации объекта, где не будет вызываться конструктор. Спецификация языка Java, §17.5.3. Последующая модификация полей final состояния:

В некоторых случаях, таких как десериализация, система должна будет изменить поля final объекта после построения. final поля могут быть изменены с помощью отражения и других зависящих от реализации средств. Единственный шаблон, в котором это имеет разумную семантику, это шаблон, в котором объект создается, а затем обновляются поля final объекта. Объект не должен быть видимым для других потоков, а также поля final не должны читаться, пока все обновления полей final объекта не будут завершены.

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

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

6
Holger

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

public  static < T > T getNewInstance ( Class <T> type )
{
    try 
    {
        return type.newInstance()  ;
    } catch ( InstantiationException | IllegalAccessException e) 
    {
        e.printStackTrace();
    }
    return null ;
}

Для глубокого клонирования объектов вы можете использовать com.rits.cloning.Cloner полезность. Например, :

private T clone(T resource){

    Cloner cloner = new Cloner();
    T cloneObject = (T) cloner.deepClone(obj);
    return cloneObject;
}
0
Varshney P.

Мы не можем создать экземпляр абстрактного класса, но мы можем сделать это в подклассе

class Teacher extends Person {

    public Teacher(Brain aBrain, int theAge) {
        super(aBrain, theAge);
    }

    protected Teacher(Person another) {
        super(another);
    }


    public Object clone() {
        return new Teacher(this);
    }
}
0
lihongxu