7

Is it possible to dynamically identify T as a return type depending on subclass Type? I want something like the following:

public class Parent {
        public <T extends Parent> T foo() {
                return (T)this;
        }
}

public class Child extends Parent {
        public void childMethod() {
                System.out.println("childMethod called");
        }
}

And then to call:

Child child = new Child();
child.foo().childMethod();

Without defining the type like so:

Child child = new Child();
child.foo().<Child>childMethod(); // compiles fine

Thanks in advance!

chaplean
  • 197
  • 1
  • 3
  • 10

2 Answers2

4

You want this:

public class Parent<T extends Parent<T>> {
    public T foo() {
        return (T)this;
    }
}

public class Child extends Parent<Child> {
    public void childMethod() {
        System.out.println("childMethod called");
    }
}

Child child = new Child();
child.foo().childMethod(); // compiles
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • simpler: public class Parent { }, class Child extends Parent { } and main : Parent instance = new Parent<>(); – solvator Jan 05 '14 at 12:41
  • 1
    @solvator No. Your code results in a raw type `Parent` in the bound, which is very undesirable – Bohemian Jan 05 '14 at 12:42
  • ahh, yeach, did not notice..it would work if there wouldn't be inheritance – solvator Jan 05 '14 at 12:46
  • 2
    @Bohemian why did you use > instead of ? – solvator Jan 05 '14 at 12:49
  • 1
    @solvator I've already answered that in my comment above, but here goes again... If you give Parent a generic parameter, *every* use of it should have that parameter, otherwise you get a *raw* type (one that hasn't been given a parameter. Although it looks like infinite recursion, `Parent>` is how you specify a generic bound to a subclass while avoiding using a raw type. When raw typing are used, all generic info is stripped from the class (almost, but not exactly, like it's `Parent`) – Bohemian Jan 05 '14 at 20:44
  • This is wrong. The `(T)this` cast is not safe. `T` extends `Parent`, but `Parent` (the type of `this`) does not extend `T`. – newacct Jan 06 '14 at 02:59
  • @newacct Actually, it is perfectly safe! In fact, `this` *must* extend `Parent`, because the "recursive" typing prevents the type `T` being anything but the declaring class, ie `Child extends Parent` is a compile error, you can only code `Child extends Parent` etc. And `this` *is* the subclass instance (not the `Parent` type, but the subclass type, `Child` in this example). There is actually no way for this to cause an error. – Bohemian Jan 06 '14 at 07:28
  • @Bohemian: You are incorrect. With `Child` declared as above, `class Foo extends Parent` is perfectly valid. Calling `.foo()` on an instance of `Foo` will cause a class cast exception. – newacct Jan 06 '14 at 08:24
  • @newacct Hmmmm, OK. I didn't consider such a deliberate misuse like that. I would be comfortable going to production with this solution. If someone is evil enough to use it like that, they deserve having their code explode. They'll quickly figure out what they've done. – Bohemian Jan 06 '14 at 09:21
0

It is impossible in the Java type system for Parent to refer to the exact class of this. However, it can have a type parameter (say T) that subclasses can specify, as either themselves, or some other type (whatever they want), and use an abstract method to delegate the task of obtaining an instance of a that type T to the subclass.

public abstract class Parent<T> {
    // the implementer is responsible for how to get an instance of T
    public abstract T getT();
    // in this case, foo() is kind of redundant
    public T foo() {
        return getT();
    }
}

public class Child extends Parent<Child> {
    public Child getT() {
        return this;
    }
    public void childMethod() {
        System.out.println("childMethod called");
    }
}

Child child = new Child();
child.foo().childMethod(); // compiles
newacct
  • 119,665
  • 29
  • 163
  • 224