Search code examples
javagenericsinheritancenested-generics

How do I extend a builder class twice using Java generics?


I have a builder class that I want to extend, here is the simple version:

class A {
    public A withSomeAStuff() {
        return this;
    }
}
A a = new A().withSomeAStuff();

When I extend it, I know that I can do this without any problem:

class AA<T extends AA> {
    public T withSomeAStuff() {
        return (T) this;
    }
}
class BB extends AA<BB> {
    public BB withSomeBStuff() {
        return this;
    }
}
AA aa = new AA().withSomeAStuff();
BB bb = new BB().withSomeAStuff().withSomeBStuff();

But now I want to extend it further with another class, so I try this:

class AAA<T extends AAA> {
    public T withSomeAStuff() {
        return (T) this;
    }
}
class BBB<T extends BBB> extends AAA<T> {
    public T withSomeBStuff() {
        return (T) this;
    }
}
class CCC extends BBB<CCC> {
    public CCC withSomeCStuff() {
        return this;
    }
}
AAA aaa = new AAA().withSomeAStuff();
BBB bbb = new BBB().withSomeAStuff().withSomeBStuff(); //breaks here!
CCC ccc = new CCC().withSomeAStuff().withSomeBStuff().withSomeCStuff();

My new CCC class works fine, but my BBB class is broken and I cannot figure out why.

What do I need to do to fix it?


Solution

  • Never ignore raw type warnings: What is a raw type and why shouldn't we use it?

    I added a method self() so you only have a single unchecked cast in your code.

    class AAA<T extends AAA<T>> {
        public T withSomeAStuff() {
            return self();
        }
    
        @SuppressWarnings("unchecked")
        protected T self() {
            return (T) this;
        }
    }
    
    class BBB<T extends BBB<T>> extends AAA<T> {
        public T withSomeBStuff() {
            return self();
        }
    }
    
    class CCC extends BBB<CCC> {
        public CCC withSomeCStuff() {
            return this;
        }
    }
    
    
    public static void main(String[] args) {
        AAA<?> aaa = new AAA<>().withSomeAStuff();
        BBB<?> bbb = new BBB<>().withSomeAStuff().withSomeBStuff(); 
        CCC ccc = new CCC().withSomeAStuff().withSomeBStuff().withSomeCStuff();
    }