clone() Method Without super.clone()

Description

clone() Method Without super.clone() is a programming error in Java where a class implements the clone() method but fails to call super.clone() when creating the cloned object. According to Java's cloning convention, all clone() implementations should obtain the new object by calling super.clone(). This ensures that the clone chain properly invokes Object.clone(), which creates a new instance of the correct runtime type. When a class violates this convention by using constructors instead of super.clone(), any subclass's clone() method will return an object of the wrong type—the parent class type instead of the actual subclass type.

Risk

Failing to call super.clone() leads to subtle but serious bugs in class hierarchies. When subclasses rely on the parent's clone() method, they receive objects of the parent type, not their own type. This causes ClassCastException at runtime when the clone is cast to the expected subclass type. The error may not manifest until subclasses are created, making it a latent defect. Applications experience unexpected runtime failures that are difficult to trace back to the improper clone implementation. Additionally, the cloned objects may have incorrect behavior if subclass-specific fields are not properly initialized.

Solution

Always call super.clone() as the first step in clone() implementations. The Object.clone() method, reached through the super.clone() chain, creates a shallow copy of the correct runtime type. After obtaining the clone, perform any necessary deep copying of mutable fields. Alternatively, consider avoiding clone() altogether and use copy constructors or static factory methods, which are more explicit and less error-prone. If using clone(), ensure the class implements Cloneable interface to avoid CloneNotSupportedException. Document the cloning behavior clearly for subclasses.

Common Consequences

ImpactDetails
IntegrityScope: Integrity

Unexpected State - Cloned objects have the wrong type, causing type mismatches and incorrect behavior in class hierarchies.
OtherScope: Other

Quality Degradation - Applications fail at runtime with ClassCastException when subclasses attempt to use the flawed clone implementation.

Example Code

Vulnerable Code

// Vulnerable: clone() without super.clone()
public class Kibitzer implements Cloneable {
    private String name;
    private int rating;

    public Kibitzer(String name, int rating) {
        this.name = name;
        this.rating = rating;
    }

    // Vulnerable: Using constructor instead of super.clone()
    @Override
    public Object clone() throws CloneNotSupportedException {
        // Wrong! Creates Kibitzer, not subclass type
        return new Kibitzer(this.name, this.rating);
    }

    public String getName() { return name; }
    public int getRating() { return rating; }
}

// Subclass that inherits broken clone
public class FancyKibitzer extends Kibitzer {
    private String title;

    public FancyKibitzer(String name, int rating, String title) {
        super(name, rating);
        this.title = title;
    }

    // This clone() will fail!
    @Override
    public Object clone() throws CloneNotSupportedException {
        // Calls parent's clone which returns Kibitzer, not FancyKibitzer!
        FancyKibitzer copy = (FancyKibitzer) super.clone();  // ClassCastException!
        // title field never gets copied
        return copy;
    }

    public String getTitle() { return title; }
}

// Vulnerable: More complex example with mutable fields
public class Person implements Cloneable {
    private String name;
    private Date birthDate;
    private List<String> nicknames;

    // Vulnerable: Constructor-based clone
    @Override
    public Object clone() {
        // Wrong approach - breaks subclass cloning
        Person copy = new Person();
        copy.name = this.name;
        copy.birthDate = new Date(this.birthDate.getTime());
        copy.nicknames = new ArrayList<>(this.nicknames);
        return copy;
    }
}

public class Employee extends Person {
    private String employeeId;
    private Department department;

    @Override
    public Object clone() {
        // This will fail - Person.clone() returns Person, not Employee
        Employee copy = (Employee) super.clone();  // ClassCastException!
        copy.employeeId = this.employeeId;
        // department is never properly cloned
        return copy;
    }
}

// Vulnerable: Using this.getClass() still wrong
public class BadCloner implements Cloneable {
    private int value;

    @Override
    public Object clone() throws CloneNotSupportedException {
        try {
            // Still wrong - relies on reflection and no-arg constructor
            BadCloner copy = this.getClass().getDeclaredConstructor().newInstance();
            copy.value = this.value;
            return copy;
        } catch (Exception e) {
            throw new CloneNotSupportedException();
        }
    }
}

// Subclass with required constructor parameters fails
public class RequiredArgsBadCloner extends BadCloner {
    private String required;

    public RequiredArgsBadCloner(String required) {
        this.required = required;
    }

    // No no-arg constructor - parent's clone() fails!
}

Fixed Code

// Fixed: Proper clone() using super.clone()
public class Kibitzer implements Cloneable {
    private String name;
    private int rating;

    public Kibitzer(String name, int rating) {
        this.name = name;
        this.rating = rating;
    }

    // Fixed: Always use super.clone()
    @Override
    public Object clone() throws CloneNotSupportedException {
        // Correct! Object.clone() creates instance of actual runtime type
        return super.clone();
        // For this class with only primitives and immutable String,
        // shallow copy from Object.clone() is sufficient
    }

    public String getName() { return name; }
    public int getRating() { return rating; }
}

// Subclass now works correctly
public class FancyKibitzer extends Kibitzer {
    private String title;

    public FancyKibitzer(String name, int rating, String title) {
        super(name, rating);
        this.title = title;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        // Now works! super.clone() returns FancyKibitzer
        FancyKibitzer copy = (FancyKibitzer) super.clone();
        // title is String (immutable) - shallow copy is fine
        return copy;
    }

    public String getTitle() { return title; }
}

// Fixed: Proper clone with mutable fields
public class Person implements Cloneable {
    private String name;
    private Date birthDate;
    private List<String> nicknames;

    public Person() {}

    public Person(String name, Date birthDate) {
        this.name = name;
        this.birthDate = birthDate;
        this.nicknames = new ArrayList<>();
    }

    // Fixed: Use super.clone() and deep copy mutable fields
    @Override
    public Object clone() throws CloneNotSupportedException {
        // First, get properly typed shallow copy
        Person copy = (Person) super.clone();

        // Deep copy mutable fields
        if (this.birthDate != null) {
            copy.birthDate = new Date(this.birthDate.getTime());
        }
        if (this.nicknames != null) {
            copy.nicknames = new ArrayList<>(this.nicknames);
        }

        return copy;
    }

    // Getters and setters...
}

// Subclass now works correctly
public class Employee extends Person {
    private String employeeId;
    private Department department;  // Assume Department is cloneable

    @Override
    public Object clone() throws CloneNotSupportedException {
        // Works! Person.clone() uses super.clone() which returns Employee
        Employee copy = (Employee) super.clone();

        // Deep copy mutable fields specific to Employee
        if (this.department != null) {
            copy.department = (Department) this.department.clone();
        }

        return copy;
    }
}

// Fixed: Complete example with proper cloning
public class Document implements Cloneable {
    private String title;
    private Date createdDate;
    private Date modifiedDate;
    private List<Section> sections;
    private Map<String, String> metadata;

    @Override
    public Document clone() {
        try {
            // Use super.clone() for correct type
            Document copy = (Document) super.clone();

            // Deep copy all mutable fields
            copy.createdDate = (Date) this.createdDate.clone();
            copy.modifiedDate = (Date) this.modifiedDate.clone();

            // Deep copy collections
            copy.sections = new ArrayList<>();
            for (Section section : this.sections) {
                copy.sections.add(section.clone());  // Section must be Cloneable
            }

            copy.metadata = new HashMap<>(this.metadata);

            return copy;

        } catch (CloneNotSupportedException e) {
            // Should never happen for Cloneable class
            throw new AssertionError("Clone not supported", e);
        }
    }
}

// Alternative: Copy constructor (often preferred over clone)
public class SafePerson {
    private final String name;
    private final Date birthDate;
    private final List<String> nicknames;

    public SafePerson(String name, Date birthDate) {
        this.name = name;
        this.birthDate = new Date(birthDate.getTime());  // Defensive copy
        this.nicknames = new ArrayList<>();
    }

    // Copy constructor - explicit and clear
    public SafePerson(SafePerson other) {
        this.name = other.name;
        this.birthDate = new Date(other.birthDate.getTime());
        this.nicknames = new ArrayList<>(other.nicknames);
    }

    // Static factory method alternative
    public static SafePerson copyOf(SafePerson other) {
        return new SafePerson(other);
    }
}

// Subclass with copy constructor
public class SafeEmployee extends SafePerson {
    private final String employeeId;
    private final Department department;

    public SafeEmployee(String name, Date birthDate,
                        String employeeId, Department department) {
        super(name, birthDate);
        this.employeeId = employeeId;
        this.department = department;  // Assume immutable or copy
    }

    // Copy constructor - clear inheritance
    public SafeEmployee(SafeEmployee other) {
        super(other);  // Copy parent fields
        this.employeeId = other.employeeId;
        this.department = new Department(other.department);  // Deep copy
    }

    public static SafeEmployee copyOf(SafeEmployee other) {
        return new SafeEmployee(other);
    }
}

// Using builder pattern for complex objects (another alternative)
public class ComplexDocument {
    private final String title;
    private final List<Section> sections;

    private ComplexDocument(Builder builder) {
        this.title = builder.title;
        this.sections = new ArrayList<>(builder.sections);
    }

    // Create copy via builder
    public Builder toBuilder() {
        return new Builder()
            .title(this.title)
            .sections(new ArrayList<>(this.sections));
    }

    public static class Builder {
        private String title;
        private List<Section> sections = new ArrayList<>();

        public Builder title(String title) {
            this.title = title;
            return this;
        }

        public Builder sections(List<Section> sections) {
            this.sections = sections;
            return this;
        }

        public ComplexDocument build() {
            return new ComplexDocument(this);
        }
    }
}

CVE Examples

No specific CVEs are commonly attributed to this CWE, as it primarily affects application correctness rather than security.


References

  1. MITRE Corporation. "CWE-580: clone() Method Without super.clone()." https://cwe.mitre.org/data/definitions/580.html
  2. Joshua Bloch. "Effective Java" - Item 13: Override clone judiciously.
  3. Oracle. "Object.clone() Documentation."