2

I try to implement a fluent interface in my 2D game engine.

Simplified example of my implementation:

public class Sprite<T> {

    protected float x = 0.0;
    protected float y = 0.0;

    public T setPosition(float x, float y) {
        this.x = x;
        this.y = y;
        return (T)this;
    }

}

public class Living<T extends Living> extends Sprite<Living> {

    protected boolean alive = false;

    public T setAlive(boolean alive) {
        this.alive = alive;
        return (T)this;
    }

}

public class Entity<T extends Entity> extends Living<Entity> {

    protected String name = null;

    public T setName(String name) {
        this.name = name;
        return (T)this;
    }

}


Entity entity = new Entity().setPosition(100, 200).setAlive(true).setName("Zombie");

I keep getting the error: "The function setAlive(boolean) does not exist."

I know, using my methods the other way round (in a more logical order) works:

Entity entity = new Entity().setName("Zombie").setAlive(true).setPosition(100, 200);

And I know that overwriting any parent setter functions in each and every child class would work:

public class Entity extends Living {

    protected String name = null;

    public Entity setPosition(float x, float y) {
        return (Entity)super.setPosition(x, y);
    }

    public Entity setAlive(boolean alive) {
        return (Entity)super.setAlive(alive);
    }

    public Entity setName(String name) {
        return (Entity)super.setName(name);
    }

}

But I want the interface to be as free/uncomplicated as possible for the 'end user' and the code to be as compact and clean as it gets.

I don't know if I just messed up the generics or my hole approach is completely wrong. I hope you can help. I am open to any advice. (Sorry for my bad english.)

Edit: I already tested the following approach and it works for the Entity class.

public class Sprite<T> {
...
}

public class Living<T> extends Sprite<T> {
...
}

public class Entity extends Living<Entity> {
...
}

I forgot to mention, that I need to instantiate Sprite & Living too. For example:

Living living = new Living().setPosition(50, 50).setAlive(false);
Poersch
  • 23
  • 4

2 Answers2

2

This is a valiant attempt at the curiously recurring template pattern in Java. The problem is that you're mixing generics and raw types which means you aren't "closing the loop" of the pattern. For example your declaration of Living:

public class Living<T extends Living> extends Sprite<Living>

Should really be:

public class Living<T extends Living<T>> extends Sprite<T>

At some point you'll need to declare a "leaf" class that resolves T, otherwise you won't be able to instantiate and declare variables of these types without resorting to raw types or wildcards (which defeats the purpose of the pattern). For example:

public final class ConcreteEntity extends Entity<ConcreteEntity>

See my answer here for more details on implementing this pattern.

Community
  • 1
  • 1
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
  • Thanks for the reply. I already tried this approach. But a forgot to mention that I also **need to instantiate Sprite & Living**. – Poersch Dec 11 '12 at 01:59
  • That won't be possible. You'll need to create an inheritance tree in which only the leaves are instantiable. That should be trivial to do though. It's what I wound up doing in the solution I mentioned in my linked answer - where normally I would've used a base class directly, instead I made a trivial offshoot class in order to resolve the recursive type parameter. – Paul Bellora Dec 11 '12 at 02:02
  • Seems like I have to add some more classes then. Thank you very much. :) – Poersch Dec 11 '12 at 04:44
0

I think your class model is over complicated, you can pass generic parameter down to the child class and then declare it explicitly:

public class Sprite<T> {
...
}

public class Living<T> extends Sprite<T> {
...
}

public class Entity extends Living<Entity> {
...
}
hoaz
  • 9,883
  • 4
  • 42
  • 53
  • Thanks for the quick reply. **Works for Entity**. But I should have mentioned that I also **need to instantiate Sprite & Living**. If I implement your approach I get the same error as above while instantiating Living like this: `Living living = new Living().setPosition(50, 50).setAlive(false);` – Poersch Dec 11 '12 at 01:57
  • you can do `new Living();` – hoaz Dec 11 '12 at 02:55
  • If I test this: `Living living = new Living().setPosition(100, 200).setAlive(true);` I get: **"cannot convert from Object to Living"** – Poersch Dec 11 '12 at 03:07
  • I think you need to have a separate class for concrete Living type, like `ConcreteLiving extends Living` for every type you are going to instantiate – hoaz Dec 11 '12 at 03:49
  • Not what I wanted to hear, but certainly the right answer. Thank you very much. :) – Poersch Dec 11 '12 at 04:42