A copy constructor creates an object from another object of the same class. It should explicitly initialize every eligible field that represents the new object’s state.

Why is this an issue?

A class can evolve after its copy constructor is written. When a new field is added but the copy constructor is not updated, the copied object silently keeps Java’s default value for that field, such as null, zero, or false. The result is an incomplete copy that can behave differently from its source and cause failures far from the constructor.

This rule raises one issue on a constructor whose sole parameter has the same type as the enclosing class when one or more eligible fields are not explicitly initialized. The declarations of the omitted fields are identified as secondary locations. Eligible fields are instance fields declared in the enclosing class that are not transient and do not have declaration initializers.

Initialize each eligible field explicitly. Any explicit write to an eligible field counts as initialization. This includes assignments of any value, such as null, zero, or false, as well as increment and decrement operations. Use an explicit assignment when a field should intentionally be reset instead of copied.

Code examples

Noncompliant code example

class Account {
  private static int accountCount;
  private String owner;
  private String note;
  private int retryCount;
  private boolean enabled;
  private transient Object cachedSession;
  private Object auditData = new Object();

  Account(Account other) { // Noncompliant: "note", "retryCount", and "enabled" are not explicitly initialized
    this.owner = other.owner;
  }
}

Compliant solution

class Account {
  private static int accountCount;
  private String owner;
  private String note;
  private int retryCount;
  private boolean enabled;
  private transient Object cachedSession;
  private Object auditData = new Object();

  Account(Account other) {
    this.owner = other.owner;
    this.note = null; // intentional reset
    this.retryCount = 0; // intentional reset
    this.enabled = false; // intentional reset
  }
}

Exceptions

Static fields do not belong to an individual object and are not considered. Fields with declaration initializers are already initialized explicitly, and transient fields often represent state that should not be copied, so the rule does not require assignments for them.

The rule does not attempt to determine whether copying a field should be shallow or deep, or whether the value assigned to a field is correct. It only checks whether each eligible field is explicitly initialized.

Explicit writes made in instance initializer blocks, directly in the constructor, through constructor delegation, or through instance helper methods called on this count when the rule can resolve the invoked code. It does not raise an issue when required semantic information or an invoked initialization path cannot be resolved.

Resources

Documentation