← Back to Core Java Course | Chapter 8: Inheritance | Lesson 7 of 7

Java Covariant Return Types

What is a Covariant Return Type?

A covariant return type lets an overriding method in a subclass declare a more specific return type than the parent method it's overriding, as long as that more specific type is itself a subtype of the parent's declared return type.

Example: What is a Covariant Return Type?

java
class Animal {}
class Dog extends Animal {}
class AnimalShelter {
	Animal adopt() { return new Animal(); }
}
class DogShelter extends AnimalShelter {
	@Override
	Dog adopt() { return new Dog(); } // more specific return type
}
public class Main {
	public static void main(String[] args) {
		System.out.println(new DogShelter().adopt() instanceof Dog);
	}
}

Covariant Returns with Custom Classes

This is especially useful for factory-style methods: a parent class's clone()-like method might return the general parent type, while a subclass override can return its own specific subclass type directly, giving callers a more precise type without any manual casting.

Example: Covariant Returns with Custom Classes

java
class Shape {
	Shape create() { return new Shape(); }
}
class Circle extends Shape {
	@Override
	Circle create() { return new Circle(); } // factory-style covariant return
}
public class Main {
	public static void main(String[] args) {
		Circle c = new Circle().create();
		System.out.println(c instanceof Circle);
	}
}

Type Safety Benefits

Without covariant returns, callers would need an explicit downcast to get back the more specific type they know they're actually receiving — covariant returns let the compiler track that precision automatically and catch type errors earlier.

Example: Type Safety Benefits

java
class Animal {}
class Dog extends Animal {}
class DogShelter {
	Dog adopt() { return new Dog(); }
}
public class Main {
	public static void main(String[] args) {
		Dog d = new DogShelter().adopt(); // no downcast needed
		System.out.println(d);
	}
}

Standard Library Covariants

Several places in the standard library use covariant returns, most notably Object.clone(), where subclasses commonly override clone() to return their own type instead of the generic Object the parent method declares.

Example: Standard Library Covariants

java
class Point implements Cloneable {
	int x = 5;
	@Override
	public Point clone() {
		try {
			return (Point) super.clone(); // covariant: returns Point, not Object
		} catch (CloneNotSupportedException e) {
			throw new RuntimeException(e);
		}
	}
}
public class Main {
	public static void main(String[] args) {
		Point p = new Point();
		Point copy = p.clone();
		System.out.println(copy.x);
	}
}

Rules for Covariant Returns

The rule only applies to reference types, not primitives — a double-returning method can't be overridden to return an int, even though int values can convert to double, because covariance is strictly about subtype relationships between reference types.

Example: Rules for Covariant Returns

java
class Base {
	Object getValue() { return "base"; }
}
class Derived extends Base {
	@Override
	String getValue() { return "derived"; } // String is a subtype of Object
}
public class Main {
	public static void main(String[] args) {
		System.out.println(new Derived().getValue());
	}
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.