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;
    }
}

ここにあるのは浅いコピーです。これは、すべてのフィールド(この場合はintString)がであるため問題ありません。プリミティブ型または不変型

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. コピーコンストラクタvs。 クローン

Javaでは、 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つの解決策を提供しました。

いつものように、チュートリアルのソースコードはGitHubから入手できます。