What was the additional information passed to the compiler using the extra <?> ?
Additional information using the wildcard <?> Was that the returned Rectangle.Builder<?> Is a superclass of all possible common classes of Rectangle.Builder<T> (see Wildcards ). And since Rectangle.Builder<T> guaranteed to have an argument of type T, which itself is a subclass of Rectangle.Builder , until its typical typing is ignored , it is also guaranteed that Rectangle.Builder<?> smallest type of Rectangle.Builder<? extends Rectangle.Builder<?>> Rectangle.Builder<? extends Rectangle.Builder<?>> . If you completely ignore generics by removing the wildcard, this information will be lost and the code will be compiled as regular pre-Java5.0 code (where generics do not exist). This is necessary for backward compatibility.
To see the difference, consider a subclass of Rectangle.Builder that ignores typical typing:
public static class BadBuilder extends Rectangle.Builder { private double height; public BadBuilder height(double height) { System.out.println("height is set"); this.height = height; return (BadBuilder) self(); } @Override public Shape.Builder opacity(double opacity) { return new Shape.Builder(); } public Rectangle build() { return new Rectangle(this); } }
Note that this class overwrites Shape.Builder#opacity without returning a subclass of itself. The compiler will not generate errors for this class (but it can warn you that the class ignores typical typing). Thus, without general information, it is legal to return a Shape.Builder type from an opacity method. Once you add a type argument to BadBuilder, this code will no longer compile:
public static class BadBuilder extends Rectangle.Builder<BadBuilder> // -> compile time error
So, the reason you get a compiler error cannot find symbol is because the Shape.Builder class itself does not declare the T Shape.Builder#heigth() method / symbol, and the declared T Shape.Builder#opacity() method guarantees just that the returned object is of type Shape.Builder , as indicated in the argument of type class Shape.Builder<T extends Shape.Builder<T>> . Therefore, calling the Rectangle.builder().opacity(0.5).height(250) method chain will only work if Rectangle.builder() really guaranteed to return the Builder that is typed by a subclass of Rectangle.Builder. And this guarantee can only be provided if typical typing is not ignored (as shown in the BadBuilder example).
When you add the Shape.Builder#heigth method by deleting the comment in the code, this error will obviously disappear, because then the Shape.Builder object returned by Shape.Builder#opacity will also have the corresponding method. You can also remove this error by re-declaring Shape.Builder#opacity in Rectangle.Builder as follows:
@Override public T opacity(double opacity) { return super.opacity(opacity); }
If you do this, it is guaranteed that the returned object T Rectangle.Builder#opacity() is of type Rectangle.Builder , as indicated in arguments of type class Rectangle.Builder<T extends Rectangle.Builder<T>> extends Shape.Builder<T> .
Hope this helps.