Search code examples
javagenericsinheritancepolymorphismdowncast

Polymorphic method return type down-casting in java


So I don't know if this is possible I've tried searching it but maybe my search terms are off. Basically I'm wondering, is there a way to create a generic function/method in a super class that returns the downcast object.

class A {
    public <downcasted type (in this example B if called from a B instance)> test() {
        return this;
    }
}

class B extends A { }

B b = new B().test()

basically having "test()" return the B instance as type B even know the function/method is declared purely in the parent class?

I know I can cast the variable, tho having many functions some of which may return Lists of the class type, etc become troublesome. I also realize I could @override the function in B and do a "return (B)this.super()" thing, but again wrapping many functions is tedious and makes makes updating the base classes code more painful.

I also know you can do

"class A<T extends A>"

and then define B as

"class B extends A<B>"

but then if you want to make a "C" that extends "B" it breaks.

So is this type of behavior possible? If so, what is it called and how do I implement it?

An example as to where this behavior could be useful would be any base data structures you want to make extendable like an N-Ary Tree that you extend into oct/quad tree structure and/or an extended class that adds a "Name" and "Attributes" or something for a xml-like node.

Edit: This seems to work(as far as the linter is concerned), it's a bit more work to implement the base methods but it's got the desired end result as far as I can tell. That said when I attempt to run it, it gives me a "cannot find symbol: class type" error. :S

static class D extends auto {

    final Class type = getClass();

    @SuppressWarnings("unchecked")
    public <T extends type> T test() {
        return (T)type.cast(this);
    }
}

static class E extends D { }
static class F extends E { }

static {
    D d = new D().test();
    E e = new E().test();
    F f = new F().test();
}

Solution

  • Update

    There is a simpler way, which seems to work:

    class Alpha {
    
        @SuppressWarnings("unchecked")
        <T extends Alpha> T test() {
            return (T) this;
        }
    }
    
    class B extends A { }
    

    However, that does not support method chaining.


    Original post

    You need test() to return a subtype of A, rather than A itself. In order to do this, the signature of the A class could be this:

    class A<T extends A<?>> {
    
        @SuppressWarnings("unchecked")
        public T test() {
            return (T) this;
        }
    }
    

    If you create a class B extending A, you will need B.test() to return an instance of B, without needing to override test() returning a specific type. You could then do something like this:

    class B<T extends B<?>> extends A<T> { }
    

    Now T is a subclass of B, and because test()'s return type is T, it will return a B instance. Further subclassing can be done in the same way:

    class C<T extends C<?>> extends B<T> { }
    

    And statements like this will work:

    C<?> c = new C<>().test();