1

This question might resemble cast the Parent object to Child object in C# but it is about C# and I have a question about Java.

I am also aiming to make a generic builder. The generic builder should only build the parts of the abstract object and the children all manage the induvidual implementations of the abstract class.

abstract class GenericBuilder<B extends GenericBuilder>
{
     //lots of build methods
     public B lastBuildingMethodInTheChain(Object someValue)
     {
          //assignment
          return this;//<-- is not allowed!
     }
}

But when I put in a cast:return (B) this; it is fine. The cast is something I want to prevent, but this also restricts the children builders from using their special methods. The last thing makes sense because the Generic type is only known at runtime, but I have no clue how to write it so that it would work at compile time.

Thank you!

RabbitBones22
  • 302
  • 4
  • 16
  • All these answers lead me to a new problem: I cannot 'have more than one child'. I am unsure if I have to edit this question or make a new question. – RabbitBones22 Jun 13 '17 at 12:26
  • I have made a new question that addresses my true problem. https://stackoverflow.com/questions/44527022/subclassing-builder-pattern-with-a-twist-in-java I will come back to this question once so it will be more usefull to others. – RabbitBones22 Jun 13 '17 at 16:34

3 Answers3

2

You can see an example of the typesafe progressive narrowing pattern (a name that I probably just made up) in various Guava builder methods. In the end, the cast is necessary, but you can hide it reasonably well like so:

@SuppressWarnings("unchecked")
private <B1 extends B> GenericBuilder<B1> me() {
  return (GenericBuilder<B1>) this;
}

used like so:

public B lastBuildingMethodInTheChain(Object someValue) {
  B self = me();
  self.assignWhatever = someValue;
  return me;
}

(I'm not 100% sure this will work exactly as-is because of the extra weirdness you've got, with the object being parameterized as a subclass of itself.)

Here are some similar examples to draw from:

Note the use of the getThis() method which avoids the cast.

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • Following some of the articles you posted, I get a Builder that does not enforce an order of arguments or makes some mandatory. I will add this to the question. – RabbitBones22 Jun 13 '17 at 14:05
1

This makes no sense. this is a GenericBuilder, and B extends GenericBuilder, so how are you going to return this (since GenericBuilder does not extend itself (tautologically)

ControlAltDel
  • 33,923
  • 10
  • 53
  • 80
1

The correct way to declare GenericBuilder is with an f-bound type:

abstract class GenericBuilder<B extends GenericBuilder<B>> {

    public B lastBuildingMethodInTheChain(Object someValue) {
        // assignment
        return (B) this;
    }
}

You can't avoid the cast, as the compiler needs it to know that this generic builder is actually the concrete builder of the generic type parameter.

You can extend the generic builder as follows:

public class ConcreteBuilder1 extends GenericBuilder<ConcreteBuilder1> {

}

It is reasonable to assume the cast is safe. The only way for the cast to fail would be to declare i.e.:

public class ConcreteBuilder2 extends GenericBuilder<ConcreteBuilder3> {

}

But why would you ever do that?

fps
  • 33,623
  • 8
  • 55
  • 110
  • I guess your answer came closest to what I was looking for. However my problem is not solved. – RabbitBones22 Jun 13 '17 at 12:31
  • @RabbitBones22 Why is your problem not solved? Now methods of the generic builder return the type of the concrete builders, allowing you to chain methods. – fps Jun 13 '17 at 15:39
  • I might be doing something wrong than, because I cannot dynamically link the child builder after the abstractBuilder. Either Java thinks that an Object is returned or I have to hard code the interface return. – RabbitBones22 Jun 13 '17 at 15:46
  • @RabbitBones22 Have you solved your problem now? Let me know if you need some help – fps Jun 13 '17 at 16:36
  • No instead I have created a new question (see my comment under the answer). Although this specific part of the problem is solved. – RabbitBones22 Jun 13 '17 at 17:19