Что не так с этим клоном ()? - PullRequest
6 голосов
/ 27 июня 2009

Я написал этот метод клонирования для случая, когда родительский класс класса Employee является абстрактным, а метод clone () в родительском классе - абстрактным. Я хотел скопировать примитивный тип данных объекта Employee с помощью этого кода вместо копирования каждый примитивный тип данных индивидуален, но в этом коде есть проблема со строкой, которую я вызываю методом clone (). (Этот код относится к классу сотрудников)

public Object clone() {
    Object obj = new Object();
    Object object = obj.clone();  //Emphasis here
    return object;

}

ошибка: метод clone () из типа Object не виден.

Но мой класс Employee находится в иерархии классов, которая может обращаться к защищенному методу clone () в классе Object.

Это мой простой класс сотрудников:

public class Employee extends Person implements Cloneable {
private int ID;

public Employee() {
    ID = 0;
}

public void setID(int ID) {
    this.ID = ID;
}

public int getID() {
    return ID;
}

public Object clone1() throws CloneNotSupportedException {
    try {
        Object obj = new Object();

        Object object = obj.clone();
        return object;
    } catch (CloneNotSupportedException ex) {
        return null;
    }
}

Ответы [ 9 ]

11 голосов
/ 27 июня 2009

Стандартный шаблон для клонирования класса:

  1. Реализация Cloneable
  2. Переопределите метод clone() и сделайте его общедоступным
  3. В clone() вызовите super.clone(), а затем скопируйте состояние любого изменяемого объекта

Вы должны , а не создать новый объект, используя new. Правильный способ - вызвать super.clone() для нового экземпляра. Object clone() является особенным и создаст новую копию объекта и скопирует его примитивные поля и ссылки.

Например:

public class Person implements Cloneable {
    protected String name;
    // Note that overridden clone is public
    public Object clone() {
        Person clone = (Person)super.clone();
        // No need to copy name as the reference will be
        // copied by Object's clone and String is immutable
        return clone;
    }
}

public class Employee extends Person {
    protected int id;
    protected java.awt.Point location;
    public Object clone() {
        Employee  clone = (Employee )super.clone();
        // No need to copy id as Object's clone has already copied it
        // Need to clone location as Point is mutable and could change
        clone.location = location.clone();
        return clone;
    }
}
6 голосов
/ 27 июня 2009

Механизм клонирования в Java несколько неловкий. Чтобы иметь возможность клонировать себя, класс должен сделать две вещи. Сначала он должен реализовать Clonable. Во-вторых, он должен переопределить clone () и сделать его общедоступным.

В вашем примере вы переопределяете clone (), но вызываете clone () не для класса Employee, а для класса Object.class (), где clone () защищен только.

5 голосов
/ 07 августа 2009

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

  • Это добавляет много кода
  • Требуется, чтобы вы перечислили все поля для копирования и сделали это
  • Это не будет работать для списков при использовании clone () (Это то, что clone () для HashMap говорит: возвращает поверхностную копию этого экземпляра HashMap: ключи и сами значения не клонируются.) Так что в итоге вы делаете это вручную (это заставляет меня плакать)

Да, и, кстати, сериализация тоже плохая, вам, возможно, придется добавить Serializable повсюду (это также заставляет меня плакать).

Так, каково решение:

библиотека Java Deep-Cloning Библиотека клонирования - это небольшая Java-библиотека с открытым исходным кодом (лицензия Apache), которая глубоко клонирует объекты. Объекты не должны реализовывать интерфейс Cloneable. По сути, эта библиотека может клонировать ЛЮБЫЕ объекты Java. Его можно использовать, например, в реализациях кэша, если вы не хотите, чтобы кэшированный объект был изменен, или когда вы хотите создать глубокую копию объектов.

Cloner cloner=new Cloner();
XX clone = cloner.deepClone(someObjectOfTypeXX);

Проверьте это на http://code.google.com/p/cloning/

4 голосов
/ 27 июня 2009

Мистер Блох из Effective Java может сказать несколько интересных слов об использовании clone.

http://www.artima.com/intv/bloch13.html

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

2 голосов
/ 27 июня 2009

Вы должны просто написать

return super.clone(); 

в вашем методе клонирования и реализации интерфейса Clonable.

2 голосов
/ 27 июня 2009

Вы реализовали интерфейс Cloneable на своем объекте?

Однако есть очень мало случаев, когда я бы использовал клон для копирования объекта. Одним из таких безопасных примеров является array.clone (). Я бы предпочел использовать идиому конструктора копирования или вручную копировать / присваивать значения.

В фоновой проблеме есть пункт № 11 в Effective Java (2-е издание). Клонируемый интерфейс - это особый вид интерфейса, поскольку он изменяет поведение класса Object в отношении клонирования. По сути, это функция, включающая интерфейс class в Java.

Редактировать: Исходя из вашего примера, вам может понадобиться обернуть вызов clone () в try-catch класса CloneNotSupportedException в общем случае.

Edit2: Перефразировал мой ответ

Edit3: Переопределили ли вы клон () в контексте public? В приведенном вами примере вы пытаетесь клонировать объект, который находится в пакете java.lang - вряд ли это пакет, в котором находится ваш код.

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

Редактировать5: Попробуйте это:

public Object clone1() throws CloneNotSupportedException {        
    return super.clone();        
}

Edit6 Затем назовите ваш метод public abstract Object copy(), например, и в реализации используйте super.clone () - чтобы избежать путаницы.

Edit7 Я сделал некоторые затмения и предложил следующее решение:

public class Cloner {
    public static abstract class Person {
       protected abstract Object clone1() throws CloneNotSupportedException;
       public Object copy() throws CloneNotSupportedException {
           return clone1();
       }
    }
    public static class Employee extends Person implements Cloneable {
        @Override
        protected Object clone1() throws CloneNotSupportedException {
            return super.clone();
        }

    }
    public static void main(String[] args) throws Exception {
        new Employee().copy();
    }
}

Но в принципе это та же концепция, что и переименование вашего абстрактного метода во что-то другое, чем clone ().

Edit8: Исправлен мой пример, теперь он работает без исключения.

(Но фактический кредит поступает на Габор Харгитай на super.clone())

0 голосов
/ 27 июня 2009

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

Интерфейс Cloneable - это интерфейс marker, используемый для оповещения виртуальной машины о том, что безопасный способ реализации метода защищенного clone () по умолчанию является копией поля за полем.

Чтобы правильно реализовать метод clone для класса, вы должны объявить открытый метод clone, например this ().

public Object clone() {
   return super.clone();
}

Хорошо работающая реализацияk создаст новый объект и правильно назначит поля в соответствии с требованиями бизнес-логики:

public Object clone() {
   CurrentClass newObject = new CurrentClass();

   newObject.field1 = this.field1; // for simple types: int, long, etc
   newObject.referenceField = this.referenceField.clone(); // for agregate objects or references.
   return newObject;
}

Заключение: объявить публичный метод клонирования. Если вы хотите иметь реализацию по умолчанию в виде копирования по полю, вызовите super и отметьте класс как Cloneable Если вы хотите только пользовательское клонирование, вы можете игнорировать метку Cloneable.

0 голосов
/ 27 июня 2009

Я не очень знаком с Java, но это может помочь: http://en.wikipedia.org/wiki/Clone_(Java_method)

Выдержка из поста:

Другим недостатком является то, что часто не может получить доступ к методу clone () на абстрактный тип. Большинство интерфейсов и абстрактные классы в Java не указать публичный метод clone (). Как результат, часто единственный способ использовать Clone () метод, если вы знаете фактический класс объекта; который вопреки принципу абстракции использования наиболее общего типа возможный. Например, если у вас есть Список ссылок на Java, нельзя вызовите clone () для этой ссылки потому что список не указывает публично метод clone (). Актуальные реализации списка, такого как ArrayList и LinkedList у всех обычно есть методы clone () сами по себе, но это неудобно и плохая абстракция, чтобы нести вокруг фактический тип класса объекта.

0 голосов
/ 27 июня 2009

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

Смотрите здесь для получения дополнительной информации: http://java.sun.com/javase/6/docs/api/java/lang/Object.html#clone()

...