Javaコピーコンストラクター

1. 前書き

Javaクラスのコピーコンストラクターは、https://www.baeldung.com/java-constructors [constructor]であり、同じJavaクラスの別のオブジェクトを使用してオブジェクトを作成します*。
これは、複数のフィールドを持つ複雑なオブジェクトをコピーする場合、または既存のオブジェクトのlink:/java-deep-copy[deep copy]を作成する場合に役立ちます。

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;
    }
}
ここにあるのはa__shallow copy_です。これは、すべてのフィールド(この場合は_int_と_String_)がlink:/java-primitives[primitive types]またはhttps:/であるため問題ありません/www.baeldung.com/java-immutable-object[immutable types]。
Javaクラスに可変フィールドがある場合、代わりにコピーコンストラクター内でlink:/java-deep-copy[_deep copy_]を作成できます。 ディープコピーでは、新しく作成されたオブジェクトは元のオブジェクトから独立しています。これは、各可変オブジェクトの個別のコピーを作成するためです。
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. コピーコンストラクターvs. クローン

Javaでは、https://www.baeldung.com/java-deep-copy [_clone_]メソッドを使用して、既存のオブジェクトからオブジェクトを作成することもできます。 ただし、コピーコンストラクタには、_clone_メソッドよりもいくつかの利点があります。
  1. コピーコンストラクターの実装ははるかに簡単です。 する必要はありません
    _Cloneable_インターフェイスを実装し、_CloneNotSupportedException_を処理します。

  2. _clone_メソッドは、一般的な_Object_参照を返します。 したがって、私たちは
    適切な型に型キャストする必要があります。

  3. _clone_メソッドの_final_フィールドに値を割り当てることはできません。
    ただし、コピーコンストラクターで行うことができます。

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);
入力オブジェクトが_Manager_クラスのインスタンスでない場合、実行時に_ClassCastException_を取得する場合があります。
コピーコンストラクターでのキャストを回避する1つの方法は、両方のクラスに新しい継承可能なメソッドを作成することです。
public class Employee {
   public Employee copy() {
        return new Employee(this);
    }
}

public class Manager extends Employee {
    @Override
    public Employee copy() {
        return new Manager(this);
    }
}
各クラスメソッドで、_this_オブジェクトの入力を使用してコピーコンストラクターを呼び出します。 このようにして、生成されたオブジェクトが呼び出し元オブジェクトと等しいことを保証できます。
Employee clone = source.copy();

5. 結論

このチュートリアルでは、いくつかのコード例を使用してコピーコンストラクターを作成する方法を示しました。 また、_clone_メソッドを避けるべきいくつかの理由について説明しました。
コピーコンストラクターを使用して、参照型が親クラスである子クラスオブジェクトを複製する場合、キャストコンストラクターに問題があります。 この問題に対する解決策を1つ提供しました。
いつものように、チュートリアルのソースコードはhttps://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-lang-oop-2[GitHubで]から入手できます。