Sarabjot
Sarabjot

Reputation: 509

Returning an objects subclass with generics

With an abstract class I want to define a method that returns "this" for the subclasses:

public abstract class Foo {
    ...
    public <T extends Foo> T eat(String eatCake) {
        ...
        return this;
    }
}  

public class Eater extends Foo {}

I want to be able to do things like:

Eater phil = new Eater();
phil.eat("wacky cake").eat("chocolate cake").eat("banana bread");

Upvotes: 23

Views: 28776

Answers (4)

Tom Hawtin - tackline
Tom Hawtin - tackline

Reputation: 147164

The tasteful approach from the client point of view (which is usually the one you want to take) is to use covariant return types which was added to support generics, as Michael Barker points out.

The slightly less tasteful, but more tasteful that a cast is to add a getThis method:

public abstract class Foo<T extends Foo<T>> {
    protected abstract T getThis();

    public T eat(String eatCake) {
        ...
        return getThis();
    }
}

public class CakeEater extends Foo<CakeEater> {
    @Override protected CakeEater getThis() {
        return this;
    }
}

Upvotes: 26

CurtainDog
CurtainDog

Reputation: 3205

An approach I've used before to achieve similar behaviour is to have the subclass pass its type into a constructor of the (generified) parent type. By way of disclaimer I was generating the subclasses on the fly and inheritence was a bit of a cheat to keep my code generation simple, as always my first instinct is to try to remove the extends relationship altogether.

Upvotes: 0

irreputable
irreputable

Reputation: 45443

public abstract class Foo<T extends Foo<T>>  // see ColinD's comment
{
    public T eat(String eatCake) 
    {
        return (T)this;
    }
}

public class CakeEater extends Foo<CakeEater> 
{
    public void f(){}
}

Edit

There is no problem to require subclass behave in a certain way that's beyond what static typing can check. We do that all the time - pages and pages of plain english to specify how you write a subclass.

The other proposed solution, with covariant return type, must do the same - asking subclass implementers, in plain english, to return the type of this. That requirement cannot be specified by static typing.

Upvotes: 30

Michael Barker
Michael Barker

Reputation: 14378

I don't think you need generics Java 5 (and later) has covariant return types, e.g.:

public abstract class Foo {
    ...
    public Foo eat(String eatCake) {
        ...
        return this;
    }
}  

public class CakeEater extends Foo {

    public CakeEater eat(String eatCake) {
        return this;
    }
}

Upvotes: 9

Related Questions