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<>();
}