This rule raises an issue when a class overrides the Object.clone method instead of resorting to a copy constructor or other copy mechanisms.

Why is this an issue?

The Object.clone / java.lang.Cloneable mechanism in Java should be considered broken for the following reasons and should, consequently, not be used:

How to fix it

A copy constructor, copy factory or a custom copy function are suitable alternatives to the Object.clone / java.lang.Cloneable mechanism.

Consider the following example:

class Entity implements Cloneable { // Noncompliant, using `Cloneable`

  public int value;
  public List<Entity> children; // deep copy wanted

  Entity() {
    EntityManager.register(this); // invariant
  }

  @Override
  public Entity clone() {
    try {
      Entity copy = (Entity) super.clone(); // invariant not enforced, because no constructor is caled
      copy.children = children.stream().map(Entity::clone).toList();
      return copy;
    } catch (CloneNotSupportedException e) { // this will not happen due to behavioral contract
      throw new AssertionError();
    }
  }
}

The Cloneable / Object.clone mechanism could easily be replaced by copy constructor:

class Entity { // Compliant

  public int value;
  public List<Entity> children; // deep copy wanted

  Entity() {
    EntityManager.register(this); // invariant
  }

  Entity(Entity template) {
    value = template.value;
    children = template.children.stream().map(Entity::new).toList();
  }
}

Or by a factory method:

class Entity { // Compliant

  public int value;
  public List<Entity> children; // deep copy wanted

  Entity() {
    EntityManager.register(this); // invariant
  }

  public static Entity create(Entity template) {
    Entity entity = new Entity();
    entity.value = template.value;
    entity.children = template.children.stream().map(Entity::new).toList();
    return Entity;
  }
}

Or by a custom copy function:

class Entity { // Compliant

  public int value;
  public List<Entity> children; // deep copy wanted

  Entity() {
    EntityManager.register(this); // invariant
  }

  public Entity copy() {
    Entity entity = new Entity();
    entity.value = value;
    entity.children = children.stream().map(Entity::new).toList();
    return Entity;
  }
}

Resources

Documentation

Related rules