skip to content

How should the return type and access modifier of an overridden clone() be declared, and why?

level: middleimportance: nice to knowfreq 35%

answer

  1. Base: protected Object clone() throws CNSE
  2. Widen protected → public
  3. Covariant return: return own type, not Object
  4. Cast inside (super.clone()→Object), no cast for callers
  5. Swallow the impossible CloneNotSupportedException

basics

~20 s

Object.clone() is protected and returns Object. When you override it, make it public so callers can use it, and use a covariant return type (return your own class) so callers don't have to cast the result.

solid answer

~40 s

On Object, clone() is declared protected with return type Object and throws CloneNotSupportedException. When a class overrides clone() to support copying it should: (1) widen access from protected to public, otherwise external code still cannot call it; and (2) use a covariant return type — return the class's own type instead of Object. Java has allowed covariant return types since Java 5, so an override may return a subtype of the overridden method's return type. This lets callers write Foo copy = foo.clone() without a cast, which is both cleaner and type-safe. Conventionally the override also drops the throws clause (or catches the exception internally) since a Cloneable class can never actually throw CloneNotSupportedException. Together these make clone() pleasant to call despite Object's awkward base signature.

go deeper

for a junior

Knows clone() should be made public and that returning your own type avoids a cast.

for a middle

Writes the canonical override: public, covariant return, internal cast of super.clone(), catch-and-rethrow of the impossible exception.

for a senior

Explains the access-widening rule, covariant returns since Java 5, and why the checked exception can be swallowed for a Cloneable class.

for a principal

Sees these as polish on an inherently flawed API and would still steer teams to copy constructors; cites covariant returns as a general override capability beyond clone.

## The base signature you are overriding On `java.lang.Object`: ```java protected Object clone() throws CloneNotSupportedException ``` Three friction points for callers: it is **protected** (not callable from outside the class/package/subclass), it returns **Object** (forces a cast), and it declares a **checked exception** (forces a try/catch even when impossible). ## Fix 1 — widen access to public Access modifiers in an override may only stay the same or **widen** (you can go protected→public, never public→protected). To let arbitrary callers clone your object you must override and declare it `public`: ```java @Override public Item clone() { ... } ``` If you leave it protected, `someItem.clone()` still won't compile from unrelated code. ## Fix 2 — covariant return type Since **Java 5**, an overriding method may declare a return type that is a **subtype** of the overridden method's return type — a **covariant return type**. Because every class is a subtype of `Object`, you can return your own class: ```java @Override public Item clone() { try { return (Item) super.clone(); } // cast needed INSIDE (super.clone() returns Object) catch (CloneNotSupportedException e) { throw new AssertionError(e); } } ``` The cast is needed *inside* the method (because `super.clone()` is typed as `Object`), but **callers** get `Item` directly: ```java Item copy = item.clone(); // no cast at the call site ``` Without covariance, every caller would write `(Item) item.clone()`. Covariant returns push that one cast into the single override and out of every call site. ## Fix 3 — drop the checked exception A class that implements `Cloneable` can never actually have `super.clone()` throw `CloneNotSupportedException`, so idiomatic overrides catch it internally and rethrow an `AssertionError`, removing the `throws` clause from the public signature. Callers then need no try/catch. ## Putting it together The conventional, caller-friendly clone override is: ```java public class Item implements Cloneable { @Override public Item clone() { // public + covariant return try { return (Item) super.clone(); } catch (CloneNotSupportedException e) { // impossible here throw new AssertionError(e); } } } ``` This trio — widen to public, covariant return, swallow the impossible exception — is what makes an inherited-from-`Object` `clone()` usable. (None of it fixes clone's deeper design problems; it just makes the *call site* clean.) ## Term definitions - **Covariant return type:** an override returning a subtype of the parent method's declared return type — permitted since Java 5. - **Widening access:** an override may make a member *more* accessible (protected→public) but never *less*; narrowing is a compile error.

  • Since which Java version are covariant return types allowed?
    Java 5 (J2SE 5.0). Before that, an override had to declare exactly the same return type as the method it overrode, so clone() overrides returned Object and callers always cast.
  • Can an override make clone() less accessible than Object's protected version?
    No. Overrides may only keep or widen access. protected can become public, but you cannot narrow it to private/package-private — that is a compile error.

saying these in an interview costs you the question

  • Leaving clone() protected and wondering why callers can't use it
  • Returning Object and forcing every caller to cast
  • Thinking you can narrow access (public→protected) in an override
  • Believing covariant return types require a special annotation

context