2

I am following examples in "Effective Java" and came across the following code:

abstract static class Builder<T extends Builder<T>>

and its implementation:

public static class Builder extends Pizza.Builder<Builder>

Why is this declared T extends Builder<T> and not T extends Builder. Is it really needed to add the template <T>? What is the impact if I just use Builder<T extends Builder>?

Micromuncher
  • 903
  • 7
  • 19
Santosh Pashupati
  • 499
  • 1
  • 6
  • 16

4 Answers4

5

It is called as "generic type". That declaration means T can be any type that is subclass of Builder<T>.

The goal of implementing Generics is finding bugs in compile-time other than in run-time. Finding bugs in compile-time can save time for debugging java program, because compile-time bugs are much easier to find and fix.

What is the impact if we just use Builder<T extends Builder>?

It transforms into raw type. And type safety goes off.

Builder<T extends Builder<T>> means that,

The class T passed in must implement the Builder interface / extend Builder class, and the generic parameter of Builder must be T itself.

3

I have some examples to show that actually the difference is not that big. I think the OP wants to know the difference between T extends Builder<T> and T extends Builder.

public abstract class Builder2<T extends Builder2> {
//doesn't compile either, because String is not a subtype of Builder2
static class WrongHouseBuilder extends Builder2<String> {}
//all ok
static class RawHouseBuilder extends Builder2 {}
static class HouseBuilder1 extends Builder2<RawHouseBuilder> {}
static class HouseBuilder2 extends Builder2<HouseBuilder1> {}
static class HouseBuilder3 extends Builder2<HouseBuilder2> {}}

Now with Builder<T>:

public abstract class Builder<T extends Builder<T>> {
//all ok
static class RawCarBuilder extends Builder {}
static class CarBuilder extends Builder<CarBuilder> {}
//ok as well, T doesn't have to be CarBuilder2
static class CarBuilder2 extends Builder<CarBuilder> {}
//doesn't compile because CarBuilder2 is not a subtype of Builder<CarBuilder2>
static class CarBuilder3 extends Builder<CarBuilder2> {}}

Of cause with T extends Builder<T>, you have more protection, but not that much.

UPDATE

Just to clarify, we should not use raw type. @Radiodef has provided an interesting example in the comment. And a quote from that answer to help you understand it:

In simpler terms, when a raw type is used, the constructors, instance methods and non-static fields are also erased.

grape_mao
  • 1,153
  • 1
  • 8
  • 16
  • Using a raw type in the bound has more drastic consequences than just what you can supply as a type argument. Using a raw type erases all the members of `T`, so [stuff like this compiles](https://ideone.com/xCeii8). Also see [*What is a raw type and why shouldn't we use it*](https://stackoverflow.com/q/2770321/2891664). – Radiodef Aug 08 '18 at 17:38
  • @Radiodef thank you for the comment, I didn't know it. When I say not that much, I mean the examples in other answers and `CarBuilder2 `, I was expecting it to be self bounded. – grape_mao Aug 08 '18 at 20:42
1

Minor: It looks more natural to me to use Builder as an interface, not an abstract class. This is a sort of recursive type declaration. It is used for type safety to prevent nasty things like the following happens:

public abstract Builder<T extends Builder<T>> {
    T build();
}


public class Entity extends Builder<String>{ // does not compile
    @Override
    public String build() {
        return null;
    }
}


public class Entity extends Builder<Entity>{ //ok
    @Override
    public Entity build() {
        return null;
    }
}

Anyway more naturally looking version (from my point of view) is:

public interface Buildable<T extends Buildable<T>> {
    T build();
}

public final class Entity implements Buildable<Entity>{
    //other methods
    @Override
    public Entity build() {
        //implementation
    }
}
St.Antario
  • 26,175
  • 41
  • 130
  • 318
  • Method `T build()` with `T extends Builder` makes no sense. – Tomasz Linkowski Aug 08 '18 at 19:33
  • @TomaszLinkowski please, expand. – St.Antario Aug 08 '18 at 19:41
  • `T extends Builder` can only return itself. So your `build` method, instead of returning something other to be built, can only return an instance of the same class. What else than `Entity.this` would your `Entity.build()` method return? Your `Entity` should not be named `Entity` - it should be named `EntityBuilder`, and its `build()` method should return an `Entity`. Right now, your `Entity` class tries to be two things at once (a builder and an entity), violating the [single responsibility principle](https://en.wikipedia.org/wiki/Single_responsibility_principle). – Tomasz Linkowski Aug 08 '18 at 20:03
  • @TomaszLinkowski Have to agree it makes a little sense. – St.Antario Aug 08 '18 at 20:13
1

I see that the question is about the <T> part in Builder<T>. Without this <T>, you simply get a raw type, and your IDE might complain.

But in my answer, I'd like to explain what's the purpose of T extends Builder<T>, because other answers do not seem to cover it (maybe you know this already).

T extends Builder<T> serves the purpose of returning appropriate Builder.this in all the Builder methods (except build() method, of course).

I usually use it with a protected abstract method like T thisInstance().

Example:

abstract class NamedBuilder<T extends NamedBuilder<T>> {
    private String name;

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

    protected abstract T thisInstance();
}

final class MoreSpecificBuilder extends NamedBuilder<MoreSpecificBuilder> {
     @Override
     protected MoreSpecificBuilder thisInstance() {
         return this;
     }
}

Thanks to such approach, you do not have to redefine name() method in all the NamedBuilder subclasses to return the specific subclass.

Without such constraint type parameter T, you would have:

 abstract class NamedBuilder {
     NamedBuilder name(String name);
 }

and you would need to override all such methods in subclasses like that:

final class MoreSpecificBuilder extends NamedBuilder {
     @Override
     MoreSpecificBuilder name(String name) {
         super.name(name);
         return this;
     }
}

EDIT: Without the constraint extends Builder<T> on type parameter T:

abstract class NamedBuilder<T> {
  // ...
}

this would work fine, although such design would be less intuitive and more error-prone.

Without such constraint, compiler would accept anything as T (e.g. String), so the constraint acts simply as a compile-time check for the implementors of NamedBuilder.

Tomasz Linkowski
  • 4,386
  • 23
  • 38
  • 1
    "Without such constraint, you would have:" No. Without the constraint, you would have `abstract class NamedBuilder {`, and your first example without the constraint would still compile fine, so it doesn't explain why you have the constraint – newacct Sep 01 '18 at 04:32
  • @newacct Good point. I confused terminology - I confused *constraint* on type parameter with a *type parameter* itself. So: 1) without the *type parameter* you'd get the behavior I described, and 2) without the *constraint* on this type parameter, `T` could represent *anything*, so you have the constraint just to make sure that `T` indeed represents `NamedBuilder`. – Tomasz Linkowski Sep 01 '18 at 09:11