skip to content

Explain the curiously recurring generic pattern (CRGP) in Java and give a use case for it.

level: seniorimportance: should knowfreq 40%

answer

  1. class X<T extends X<T>>, subclass extends X<Subclass>
  2. Lets base methods return the concrete subtype
  3. self() = (T) this, unchecked cast
  4. Flagship use: inheritable fluent builders
  5. Convention only — wrong-type subclass still compiles

basics

~20 s

A class declares itself as its own type parameter, like 'class Node<T extends Node<T>>'. Subclasses pass themselves in. It lets base-class methods refer to the actual subclass type — useful for fluent builders that return the right subtype.

solid answer

~50 s

The curiously recurring generic pattern (CRGP, the Java analogue of C++'s CRTP) is a class declared with a self-referential bound where subclasses parameterize the base with themselves: 'class AbstractBuilder<T extends AbstractBuilder<T>>' and 'class ConcreteBuilder extends AbstractBuilder<ConcreteBuilder>'. This lets methods in the base class know the concrete subtype T, so they can return T instead of the base type. The classic use is a fluent builder hierarchy: a shared base defines common setters that return 'self()' typed as T, so chaining preserves the concrete builder type and you can still call subclass-specific methods after a base setter. Other uses include self-typed comparable/entity hierarchies and generic DSLs. The catch is that nothing forces a subclass to pass its own type — 'class Evil extends AbstractBuilder<SomethingElse>' compiles — so it's a convention, not an ironclad guarantee, usually paired with an abstract 'T self();' implemented as 'return this;'.

code

java · 14 lines
java
abstract class Builder<T extends Builder<T>> {
    @SuppressWarnings("unchecked")
    protected T self() { return (T) this; }
    T common(String v) { /* set field */ return self(); }
}

class UserBuilder extends Builder<UserBuilder> {
    UserBuilder name(String n) { /* ... */ return this; }
}

// Chaining preserves the concrete type:
UserBuilder b = new UserBuilder()
        .common("x")   // returns UserBuilder, not Builder
        .name("Ann");  // subclass method still reachable

go deeper

for a junior

Recognizes the unusual 'class X<T extends X<T>>' shape and that it relates to builders, even if the why is fuzzy.

for a middle

Explains it makes base methods return the concrete subtype and can sketch a fluent-builder example.

for a senior

Implements it correctly with self()/abstract self(), names real-world users, and explains it's convention-not-guarantee with the unchecked cast.

for a principal

Weighs the API ergonomics cost (signature noise, leaky generic) vs. benefit, knows when to avoid it, and relates it to CRTP and true self-types in other languages.

## The problem CRGP solves Suppose you have a base class with methods that should return the *most specific* subtype so callers can keep chaining. A non-generic base can only return its own type: ```java class Animal { Animal eat() { ...; return this; } } class Dog extends Animal { Dog bark() { ...; return this; } } new Dog().eat().bark(); // COMPILE ERROR: eat() returns Animal, which has no bark() ``` The base method `eat()` returns `Animal`, so the static type after `.eat()` is `Animal`, and `Animal` has no `bark()`. We want `eat()` to return `Dog` when called on a `Dog`. ## The pattern **Curiously Recurring Generic Pattern (CRGP)** — also called the **self-type idiom**, and the Java cousin of C++'s **CRTP** (Curiously Recurring Template Pattern) — solves this by making the base generic over a type that *is itself*: ```java abstract class Animal<T extends Animal<T>> { @SuppressWarnings("unchecked") protected T self() { return (T) this; } T eat() { /* ... */ return self(); } } class Dog extends Animal<Dog> { // passes itself as T Dog bark() { /* ... */ return this; } } new Dog().eat().bark(); // OK: eat() returns Dog ``` Read the bound `<T extends Animal<T>>` as: "T is some subtype of Animal that is parameterized by itself." When `Dog extends Animal<Dog>`, inside `Animal` the placeholder `T` *is* `Dog`, so `eat()` returns `Dog`. ## Why `self()`? Inside the base you have `this`, whose static type is `Animal<T>`, not `T`. You can't return `this` directly where `T` is expected, so you cast: `(T) this`. That cast is unchecked (the compiler can't verify the subclass actually passed itself), hence `@SuppressWarnings`. A common cleaner variant declares `protected abstract T self();` and each leaf implements `return this;`, moving the (now safe) cast out of the base. ## The canonical use case: fluent builders ```java abstract class PizzaBuilder<T extends PizzaBuilder<T>> { protected final List<String> toppings = new ArrayList<>(); @SuppressWarnings("unchecked") protected T self() { return (T) this; } T addTopping(String t) { toppings.add(t); return self(); } } class DeluxePizzaBuilder extends PizzaBuilder<DeluxePizzaBuilder> { DeluxePizzaBuilder stuffedCrust() { /* ... */ return this; } } new DeluxePizzaBuilder() .addTopping("cheese") // returns DeluxePizzaBuilder, not PizzaBuilder .stuffedCrust() // subclass method still reachable .addTopping("olives"); ``` Without CRGP, `addTopping` would return the base type and `.stuffedCrust()` would be unreachable in the chain. CRGP is the standard way to build inheritable fluent APIs (Spring Security, AssertJ, jOOQ use this shape). ## Limits and gotchas 1. **It's a convention, not a guarantee.** `class Liar extends Animal<Dog>` compiles even though `Liar` is not `Dog`; `self()` would then throw `ClassCastException`. The bound prevents passing a *non-Animal*, but not passing the *wrong* Animal. 2. **The unchecked cast** is inherent; document it. 3. **Type-signature noise** — every consumer of the hierarchy sees the `<T>` parameter, which hurts readability. For a single-level builder you don't need it at all. 4. **No true `self` type in Java.** Languages like Scala/Swift have a real `Self` type; CRGP is the workaround. Erasure means it's purely compile-time machinery. ## Summary CRGP = a class parameterized by a self-referential bound (`X<T extends X<T>>`) with subclasses passing themselves, so base methods can speak in terms of the concrete subtype. The flagship application is inheritable fluent builders; the price is an unchecked self-cast and a subtype contract enforced only by convention.

  • How does CRGP differ from C++'s CRTP?
    CRTP uses templates resolved at compile time to enable static polymorphism (the base can call subclass methods with no virtual dispatch). Java CRGP is about type-level self-reference for return types; Java still uses normal virtual dispatch and erases the generics, so there's no static-polymorphism performance angle.
  • Why is the self() cast unchecked and can it ever fail?
    Because the compiler can't verify the subclass parameterized the base with its own type. It fails with ClassCastException only if a subclass lies (extends X<SomethingElse>). With the disciplined convention it never fires.

saying these in an interview costs you the question

  • Claiming the bound forces a subclass to pass its own type (it doesn't — Liar extends X<Other> compiles)
  • Saying it works at runtime via reflection — it's pure compile-time, generics are erased
  • Confusing it with simple bounded generics; CRGP specifically self-parameterizes the class
  • Using it for a one-level builder where it adds noise for no benefit

context