«1. Введение

Конструктор копирования в классе Java — это конструктор, который создает объект, используя другой объект того же класса Java.

Это полезно, когда мы хотим скопировать сложный объект с несколькими полями или когда мы хотим сделать глубокую копию существующего объекта.

2. Как создать конструктор копирования

Чтобы создать конструктор копирования, мы можем сначала объявить конструктор, который принимает объект того же типа в качестве параметра:

public class Employee {
    private int id;
    private String name;
  
    public Employee(Employee employee) {
    }
}

Затем мы копируем каждое поле входного объекта в новый экземпляр:

public class Employee {
    private int id;
    private String name;
    
    public Employee(Employee employee) {
        this.id = employee.id;
        this.name = employee.name;
    }
}

Здесь у нас есть неглубокая копия, и это нормально, поскольку все наши поля — int и String в данном случае — являются примитивными типами или неизменяемые типы.

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

public class Employee {
    private int id;
    private String name;
    private Date startDate;

    public Employee(Employee employee) {
        this.id = employee.id;
        this.name = employee.name;
        this.startDate = new Date(employee.startDate.getTime());
    }
}

3. Конструктор копирования или клонирование

В Java мы также можем использовать метод clone для создания объекта из существующего объекта. Однако конструктор копирования имеет некоторые преимущества перед методом клонирования:

  1. The copy constructor is much easier to implement. We do not need to implement the Cloneable interface and handle CloneNotSupportedException.
  2. The clone method returns a general Object reference. Therefore, we need to typecast it to the appropriate type.
  3. We can not assign a value to a final field in the clone method. However, we can do so in the copy constructor.

4. Вопросы наследования

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

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

public class Manager extends Employee {
    private List<Employee> directReports;
    // ... other constructors

    public Manager(Manager manager) {
        super(manager.id, manager.name, manager.startDate);
        this.directReports = directReports.stream()
          .collect(Collectors.toList());
    }
}

Затем мы объявим переменную Employee и создадим ее экземпляр с помощью конструктора Manager:

Employee source = new Manager(1, "Baeldung Manager", startDate, directReports);

Поскольку ссылка тип — Employee, мы должны привести его к типу Manager, чтобы мы могли использовать конструктор копирования класса Manager:

Employee clone = new Manager((Manager) source);

Мы можем получить ClassCastException во время выполнения, если входной объект не является экземпляром класса Manager.

Один из способов избежать приведения типов в конструкторе копирования — создать новый наследуемый метод для обоих классов:

public class Employee {
   public Employee copy() {
        return new Employee(this);
    }
}

public class Manager extends Employee {
    @Override
    public Employee copy() {
        return new Manager(this);
    }
}

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

Employee clone = source.copy();

5. Заключение

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

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

Как всегда, исходный код руководства доступен на GitHub.