0

I have three classes

abstract class Animal {...}
class Shark extends Animal {...}
class Hammerhead extends Shark {...}

Each of these classes has a static inner _Builder class. These are standard builder classes, but they extend from each other to allow full configuration of the object at instantiation. e.g. the use of the builders could look something like

Hammerhead.getBuilder().setColor(color.GREY).setFinSize(1.3f).setHammerSize(.9f).build();

which would return a new instance of Hammerhead

Initially I had this issue where the above would not work because for example setColor() would return an Animal.ABuilder which I could not subesquently call setFinSize() on (It is a method of Shark.Sbuilder).

This was remedied by making the _Builders into generics as seen in the answer to this question

The answer worked well for extending one child, but I am having issues extending beyond that to a grandchild builder.

So here are the class signatures for the builders as I have them
static class ABuilder<B extends ABuilder<B>>{...}
static class SBuilder<B extends SBuilder<B>> extends ABuilder<B extends ABuilder<B>>{...}
static class HBuilder<B extends HBuilder<B>> extends SBuilder<B extends SBuilder<B>>{...}

Unfortunately this is giving me compilation errors.

Type Parameter 'B' is not within it's bounds; should extend 'Animal.ABuilder'

This error is directed at the third and final B in the SBuilder definition

Is there a way to achieve this?

Thanks!

Sources
Animal.java

package com.example;

public abstract class Animal {

    String color;

    public Animal(ABuilder builder){
        this.color = builder.color;
    }

    public static class ABuilder<B extends ABuilder<B>> {
        String color;
        public B setColor(String c){
            color = c;
            return (B)this;
        }
    }
}

Shark.java

package com.example;

public class Shark extends Animal {

    float fin_size;

    public Shark(SBuilder builder){
        super(builder);
        fin_size = builder.fins;
    }

    //                                                 Problems are in this general area: \/
    public static class SBuilder<B extends SBuilder<B>> extends ABuilder<B extends ABuilder<B>>{ 

        float fins;

        public B setFinSize(float size){
            this.fins = size;
            return (B) this;
        }

        public Shark build(){
            return new Shark(this);
        }
    }
}

Hammerhead.java

package com.example;

public class Hammerhead extends Shark {

    float hammer;

    public Hammerhead(HBuilder builder) {
        super(builder);
        hammer = builder.hammer;
    }

    public static HBuilder<? extends  HBuilder> getBuilder(){
        return new HBuilder<HBuilder>();
    }

      public static class HBuilder<B extends HBuilder<B>> extends SBuilder<B extends SBuilder<B>> { {

        float hammer;

        public B setHammerSize(float hammer) {
            this.hammer = hammer;
            return (B)this;
        }

        public Hammerhead build(){
            return new Hammerhead(this);
        }
    }
}

Edit

Per the answer from Seelenvirtuose:

Your answer makes sense to me. I thought I had the problem solved, but now that I have made the changes, it is still not working as intended:

After the first call to setColor() the returned type (as reported by Intellij IDEA) is just whatever class the function is defined in.

Comments on each line are the type returned by the function call

Hammerhead hh =
    Hammerhead.getBuilder() // HBuilder<? extends HBuilder> //
        .setColor("Black") // ? extends com.example.Hammerhead.HBuilder
        .setHammerSize(10f) // HBuilder
        .setFinSize(10f) // SBuilder
        .setColor("Fuschia") // ABuilder

        .setFinSize(10f) // Error: No such method on ABuilder

        .build(); // Also does not work because build()
                  // should be called on an HBuilder

Edit 2

The solution works. After some fuddling I realized I had a second issue with my getBuilder() method which was causing the further problems. I will edit the above when I get home to reflect the proper function and solution.

final edit

The getBuilder function should have been

    public static HBuilder<? extends  HBuilder<?>> getBuilder(){
        return new HBuilder<>();
    }
Community
  • 1
  • 1

1 Answers1

1

Well, your Animal class has the following builder class:

public static class ABuilder<B extends ABuilder<B>> { ... }

The purpose of the type parameter B is to bind any subtype of ABuilder to this type variable. But you do not use this subtype binding properly in the subclasses.

Simply change your shark builder and your hammerhead builder to:

public static class SBuilder<B extends SBuilder<B>> extends ABuilder<B> { ... }

public static class HBuilder<B extends HBuilder<B>> extends SBuilder<B> { ... }

Since B is already extending SBuilder (HBuilder, respectively), there is no need to extend anything more than ABuilder<B>.

Seelenvirtuose
  • 20,273
  • 6
  • 37
  • 66
  • Thanks for the response, Seelenvirtuose. This answer looked good to me, but unfortunately even after your advice it is not acting as I would expect. Please see the edit on my question for details. – Jake Thomas Mar 16 '16 at 19:16