SS44
SS44

Reputation: 857

Recursive Generic and Fluent Interface

tl;dr

Trying to implement a hierarchal fluent interface such that I can combine nodes child classes while also the class standalone, but getting type parameter is not within its bound errors.

Details

I'm trying to achieve a solution so that I can create something such that I can do something like:

farm
    .animal()
        .cat()
            .meow()
            .findsHuman()
                .saysHello()
                .done()
            .done()
        .dog()
            .bark()
            .chacesCar()
            .findsHuman()
                .saysHello()
                .done()
            .done()
        .done()
    .human()
        .saysHello()
        .done();

while also being able to do:

Human human = new Human()
    .saysHello()

I've gotten close using various strategies but haven't been able to gain the flexibility described.

My current attempt uses the following classes:

abstract class Base<T extends Base<T>>{

    private T parent;

    Base(){

    }

    Base( T parent ){
        this.parent = parent;
    }

    public T done() throws NullPointerException{
        if ( parent != null ){
            return (T) parent;
        }

        throw new NullPointerException();
    }   
}

class Farm<T extends Base<T>> extends Base{

    private Animal<Farm<T>> animal;
    private Human<Farm<T>> human;

    public Farm(){
        super();
        this.animal = new Animal( this );
        this.human = new Human( this );
    }

    public Animal<Farm> animal(){
        return this.animal;
    }

    public Human<Farm<T>> human(){
        return this.human;
    }
}

class Animal <T extends Base<T>> extends Base{

    private Cat<Animal<T>> cat;
    private Dog<Animal<T>> dog;

    public Animal(){
        super();
        init();
    }

    public Animal( T parent ){
        super( parent );
        init();
    }

    private void init(){
        this.cat = new Cat(this);
        this.dog = new Dog(this);
    }

    public Cat<Animal<T>> cat(){
        return cat;
    }

    public Dog<Animal<T>> dog(){
        return dog;
    }
}

class Human<T extends Base<T>> extends Base{

    public Human<T> saysHello(){
        System.out.println("human says hi");
        return this;
    }
}

class Cat <T extends Base<T>> extends Base{

    private Human<Cat> human;

    public Cat(){
        super();
        init();
    }

    public Cat( T parent ){
        super( parent );
        init();
    }

    private void init(){
        this.human = new Human();
    }

    public Cat<T> meow(){
        System.out.println("cat says meow");
        return this;
    }

    public Human<Cat<T>> findsHuman(){
        return this.human;
    }
}


class Dog <T extends Base<T>> extends Base{

    private Human<Dog> human;

    public Dog(){
        super();
        init();
    }

    public Dog( T parent ){
        super( parent );
        init();
    }

    private void init(){
        this.human = new Human();
    }


    public Dog<T> bark(){
        System.out.println("dog says woof");
        return this;
    }

    public Dog<T> chacesCar(){
        System.out.println("cat drinks milk");
        return this;
    }

    public Human<Dog<T>> findsHuman(){
        return this.human;
    }

}

The errors I'm seeing are commonly:

Animal.java:4: type parameter Animal is not within its bound private Cat cat; Animal.java:5: type parameter Animal is not within its bound private Dog dog;

Applied to all similar references and also pertaining to my example desired case:

cannot find symbol symbol : method dog() location: class Base.dog()

I've tried using the following solutions which seemed to tackle similar problems, but to no avail, so any and all support is welcome.

References

Upvotes: 9

Views: 2446

Answers (7)

Serge Ballesta
Serge Ballesta

Reputation: 149075

Your problem is that the method done should return the parent, but the parent is not necessarily a T but is just a Base. And the other problem is that whatever the class is, the done method should always return the same class.

But here is a slight variation of your proposed classes. First for Base declaring its concrete class and its concrete parent :

abstract class Base<T extends Base<T, P>, P>{

    private P parent;

    Base(){

    }

    Base( P parent ){
        this.parent = parent;
    }

    public P done() throws NullPointerException{
        if ( parent != null ){
            return parent;
        }

        throw new NullPointerException();
    }   
}

That being done, the derived concrete classes become :

class Farm extends Base<Farm, Object>{

    private Animal animal;
    private Human human;

    public Farm(){
        super();
        this.animal = new Animal( this );
        this.human = new Human( this );
    }

    public Animal animal(){
        return this.animal;
    }

    public Human human(){
        return this.human;
    }
}

class Animal extends Base<Animal, Farm>{

    private Cat cat;
    private Dog dog;

    public Animal(){
        super();
        init();
    }

    public Animal( Farm parent ){
        super( parent );
        init();
    }

    private void init(){
        this.cat = new Cat(this);
        this.dog = new Dog(this);
    }

    public Cat cat(){
        return cat;
    }

    public Dog dog(){
        return dog;
    }
}

class Human extends Base<Human, Farm>{

    public Human() {

    }

    public Human(Farm farm) {
        super(farm);
    }

    public Human saysHello(){
        System.out.println("human says hi");
        return this;
    }

}

class CatOrDog extends Base<Cat, Animal>{

    protected Human human;

    public CatOrDog(){
        super();
        init(null);
    }

    public CatOrDog( Animal parent ){
        super( parent );
        init(parent);
    }

    private void init(Animal parent){
        Animal parent = done();
        Farm farm = (parent == null) ? null : parent.done();
        this.human = new Human(farm);
    }

    public Human findsHuman(){
        return this.human;
    }
}


class Cat extends CatOrDog{

    public Cat(){
        super();
    }

    public Cat( Animal parent ){
        super( parent );
    }

    public Cat meow(){
        System.out.println("cat says meow");
        return this;
    }
}


class Dog extends CatOrDog {

    public Dog(){
        super();
    }

    public Dog( Animal parent ){
        super( parent );
    }

    public Dog bark(){
        System.out.println("dog says woof");
        return this;
    }

    public Dog chacesCar(){
        System.out.println("cat drinks milk");
        return this;
    }
}

With that, I could write without any error or warning :

Farm farm = new Farm();
farm.animal()
    .cat()
        .meow()
        .findsHuman()
            .saysHello()
            .done()
        .animal()
    .dog()
        .bark()
        .chacesCar()
        .findsHuman()
            .saysHello()
            .done()
        .animal()
    .done()
.human()
    .saysHello()
    .done();

But note that I had to replace to done calls with animals calls.

Edit :

I added a new class CatOrDog to factorize the Human processing. As the parent of a Human is a Farm, I initialize the new human with a correct parent if it exists. That way, not only the above sources compiles without error or warning, but it also runs without any problem and it prints :

cat says meow
human says hi
dog says woof
cat drinks milk
human says hi
human says hi

Upvotes: 1

Constantinos
Constantinos

Reputation: 1208

The code below seems to work fine and doesn't need any @SuppressWarnings. The key concept to grasp is that your T parameter is effectively the class of your object's parent, but T's parent could be anything. So instead of T extends Base<T> you want T extends Base<?>.

The output is:

cat says meow
human says hi
dog says woof
cat drinks milk
human says hi
human says hi

...which I believe is correct, although you might want to change your Dog.chacesCar() method so it doesn't output cat drinks milk! Also it should be chases not chaces.

Hope this helps!

abstract class Base<T extends Base<?>> {

    private final T parent;

    Base() {
        this.parent = null;
    }

    Base(T parent) {
        this.parent = parent;
    }

    public T done() throws NullPointerException {
        if (parent != null) {
            return parent;
        }

        throw new NullPointerException();
    }
}

class Farm<T extends Base<?>> extends Base<T> {

    private final Animal<Farm<T>> animal;
    private final Human<Farm<T>> human;

    public Farm() {
        super();
        this.animal = new Animal<>(this);
        this.human = new Human<>(this);
    }

    public Animal<Farm<T>> animal() {
        return this.animal;
    }

    public Human<Farm<T>> human() {
        return this.human;
    }
}

class Animal<T extends Base<?>> extends Base<T> {

    private Cat<Animal<T>> cat;
    private Dog<Animal<T>> dog;

    public Animal() {
        super();
        init();
    }

    public Animal(T parent) {
        super(parent);
        init();
    }

    private void init() {
        this.cat = new Cat<>(this);
        this.dog = new Dog<>(this);
    }

    public Cat<Animal<T>> cat() {
        return cat;
    }

    public Dog<Animal<T>> dog() {
        return dog;
    }
}

class Human<T extends Base<?>> extends Base<T> {
    public Human() {
        super();
    }

    public Human(T parent) {
        super(parent);
    }

    public Human<T> saysHello() {
        System.out.println("human says hi");
        return this;
    }
}

class Cat<T extends Base<?>> extends Base<T> {

    private Human<Cat<T>> human;

    public Cat() {
        super();
        init();
    }

    public Cat(T parent) {
        super(parent);
        init();
    }

    private void init() {
        this.human = new Human<>(this);
    }

    public Cat<T> meow() {
        System.out.println("cat says meow");
        return this;
    }

    public Human<Cat<T>> findsHuman() {
        return this.human;
    }
}

class Dog<T extends Base<?>> extends Base<T> {

    private Human<Dog<T>> human;

    public Dog() {
        super();
        init();
    }

    public Dog(T parent) {
        super(parent);
        init();
    }

    private void init() {
        this.human = new Human<>(this);
    }

    public Dog<T> bark() {
        System.out.println("dog says woof");
        return this;
    }

    public Dog<T> chacesCar() {
        System.out.println("cat drinks milk");
        return this;
    }

    public Human<Dog<T>> findsHuman() {
        return this.human;
    }

}

Test code:

public static void main(String[] args) {
    Farm<?> farm = new Farm<>();
    farm
        .animal()
            .cat()
                .meow()
                .findsHuman()
                    .saysHello()
                    .done()
                .done()
            .dog()
                .bark()
                .chacesCar()
                .findsHuman()
                    .saysHello()
                    .done()
                .done()
            .done()
        .human()
            .saysHello()
            .done();

    Human human = new Human()
            .saysHello();
}

Upvotes: 4

morpheus05
morpheus05

Reputation: 4872

The best thing I came up is the following:

new Animal()
    .cat()
      .meow()
      .findsHuman()
        .<Cat>done()
      .<Animal>done()
    .dog()
      .bark()
        .findHuman()
          .<Dog>done()
      .done();

With the following base class:

public abstract class Base<T extends Base<T>>{

  private Base<?> backRef;

  public Base() {}

  public Base(Base<?> backRef) {
    this.backRef = backRef;
 }

  @SuppressWarnings("unchecked")
    protected T self() {
    return (T)this;
  }

  @SuppressWarnings("unchecked")
  public <U extends Base<U>> U done() {
    return (U)backRef;
  }
}

If you declare backRef as of Type T then the other classes are not allowed because they are not a subclasses of each other, so you have to specify a different type, but since this type is context dependent (one time its Cat, one time its Dog) I don't see an alternative as to pass a hint.

I found a solution:

new Animal()
    .cat()
      .meow()
      .findsHuman()
        .done()
      .done()
    .dog()
      .bark()
        .findHuman()
          .done()
    .done();



public abstract class Base<T extends Base<T,P>, P>{

  private P backRef;
  public Base() {}

  public Base(P backRef) {
    this.backRef = backRef;
  }

  @SuppressWarnings("unchecked")
  protected T self() {
    return (T)this;
  }

  public P done() {
    return backRef;
 }
}

Like someone suggested, we add an additional Type for the parent.

Now the base classes:

public final class Cat extends Base<Cat, Animal>{

  public Cat() {}

  public Cat(Animal backRef) {
    super(backRef);
  }

  public Cat meow() {
    System.out.println("Meeeoooww");
    return self();
  }

  public Human<Cat> findsHuman() {
    return new Human<Cat>(this);
  }
}

As you can see, Cat clearly specifies which base type it should use. Now for human, which can change the type depending on the context:

public final class Human<P> extends Base<Human<P>, P> {

  public Human() {}

  public Human(P backRef) {
    super(backRef);
  }

}

Human specifies an additional generic which the caller (Cat, Dog) specifies in their findHuman() Method.

Upvotes: 3

sp00m
sp00m

Reputation: 48837

You could also play with interfaces, so that you can fake multiple inheritance. A bit verbose, but there is no hazardous casting, and I find it quite understandable.


Define the available methods:

public interface AnimalIn {
    AnimalOut animal();
}

public interface CatIn {
    CatOut cat();
}

public interface MeowIn {
    CatOut meow();
}

public interface DogIn {
    DogOut dog();
}

public interface BarkIn {
    DogOut bark();
}

public interface ChacesCarIn {
    DogOut chacesCar();
}

public interface FindsHumanIn<T> {
    HumanOut<T> findsHuman();
}

public interface HumanIn {
    HumanOut<FarmOut> human();
}

public interface SaysHelloIn<T> {
    HumanOut<T> saysHello();
}

public interface DoneIn<T> {
    T done();
}

You may need to have multiple methods in an interface, but I haven't met this need yet. For example, if you had had to kinds of meows:

public interface MeowIn {
    CatOut meowForFood();
    CatOut meowForMilk();
    CatOut meowForStrokes();
}

Define the output types:

Farm provides Animal or Human:

public interface FarmOut extends AnimalIn, HumanIn {
    // no specific methods
}

Animal provides Cat, Dog or Done:

public interface AnimalOut extends CatIn, DogIn, DoneIn<FarmOut> {
    // no specific methods
}

Cat provides Meow, FindsHuman or Done:

public interface CatOut extends MeowIn, FindsHumanIn<CatOut>, DoneIn<AnimalOut> {
    // no specific methods
}

Dog provides Bark, ChacesCar, FindsHuman or Done:

public interface DogOut extends BarkIn, ChacesCarIn, FindsHumanIn<DogOut>, DoneIn<AnimalOut> {
    // no specific methods
}

Human provides SayHello or Done:

public interface HumanOut<T> extends SaysHelloIn<T>, DoneIn<T> {
    // no specific methods
}

Simply implement the *Out interfaces:

public class Farm implements FarmOut {

    @Override
    public AnimalOut animal() {
        return new Animal(this);
    }

    @Override
    public HumanOut<FarmOut> human() {
        return new Human<FarmOut>(this);
    }

}

public class Animal implements AnimalOut {

    private FarmOut chain;

    public Animal(FarmOut chain) {
        this.chain = chain;
    }

    @Override
    public CatOut cat() {
        return new Cat(this);
    }

    @Override
    public DogOut dog() {
        return new Dog(this);
    }

    @Override
    public FarmOut done() {
        return chain;
    }

}

public class Dog implements DogOut {

    private AnimalOut chain;

    public Dog(AnimalOut chain) {
        this.chain = chain;
    }

    @Override
    public DogOut bark() {
        System.out.println("bark");
        return this;
    }

    @Override
    public DogOut chacesCar() {
        System.out.println("chaces car");
        return this;
    }

    @Override
    public HumanOut<DogOut> findsHuman() {
        return new Human<DogOut>(this);
    }

    @Override
    public AnimalOut done() {
        return chain;
    }

}

public class Cat implements CatOut {

    private AnimalOut chain;

    public Cat(AnimalOut chain) {
        this.chain = chain;
    }

    @Override
    public CatOut meow() {
        System.out.println("meow");
        return this;
    }

    @Override
    public HumanOut<CatOut> findsHuman() {
        return new Human<CatOut>(this);
    }

    @Override
    public AnimalOut done() {
        return chain;
    }

}

public class Human<T> implements HumanOut<T> {

    private T chain;

    public Human(T chain) {
        this.chain = chain;
    }

    @Override
    public HumanOut<T> saysHello() {
        System.out.println("hello");
        return this;
    }

    @Override
    public T done() {
        return chain;
    }

}

Those implementations would work also without the interfaces: remove the implements *Out, the @Overrides, and replace any *Out by * (e.g. AnimalOut by Animal). That said, it's easier to maintain with the interfaces: simply update them and fix your compilation errors. It's also easier to find DSL solutions with interfaces (as you can see), and they are sometimes simply necessary.


Demo:

new Farm()
.animal()
    .cat()
        .meow()
        .findsHuman()
            .saysHello()
            .done()
        .done()
    .dog()
        .bark()
        .chacesCar()
        .findsHuman()
            .saysHello()
            .done()
        .done()
    .done()
.human()
    .saysHello()
    .done();

Prints:

meow
hello
bark
chaces car
hello
hello

Upvotes: 1

Pavel Horal
Pavel Horal

Reputation: 18204

This is what we did on one our project:

public abstract class Parent<T extends Parent<T>> {

    /**
     * Get {@code this} casted to its subclass.
     */
    @SuppressWarnings("unchecked")
    protected final T self() {
        return (T) this;
    }

    public T foo() {
        // ... some logic
        return self();
    }

    // ... other parent methods

}

public class Child extends Parent<Child> {

    public Child bar() {
        // ... some logic
        return self();
    }

    // ... other child methods

}

Allowing child to have its own subclass would be:

public class Child<T extends Child<T>> extends Parent<T> {

    public T bar() {
        // ... some logic
        return self();
    }

}

Upvotes: 2

Sean Patrick Floyd
Sean Patrick Floyd

Reputation: 299048

There is no "safe" way to do this, but this should compile:

class Dog extends Base{

 <T extends Dog> T bark(){
    return (T) this;
 } 

}

Upvotes: 0

Tetramputechture
Tetramputechture

Reputation: 2921

In this line:

class Farm<T extends Base<T>>

The compiler treats the second type parameter as a concrete class. For example, say if you replaced that line with this:

class Farm<T extends Base<Double>>

'Double' is a concrete class. When the compiler scans this, it cannot tell the difference between your T and Double, and such treats them both as concrete class, and not type parameters. The only way to let the compiler know T is a type parameter is this way:

class Farm<T extends Base<T>, T>

I hope this answers (or is at least relevant) to your question.

edit Post was edited while I was typing, so I guess this answer isn't relevant anymore.

Upvotes: 1

Related Questions