What was the additional information passed to the compiler using the additional
<?>
?
The additional information by using a wildcard <?>
was, that the returned Rectangle.Builder<?>
is the super class of all possible generic Rectangle.Builder<T>
classes (see Wildcards). And since Rectangle.Builder<T>
is guaranteed to have a type argument T, that is itself a subclass of Rectangle.Builder
, as long as its generic typing is not ignored, Rectangle.Builder<?>
is also guaranteed to be at least of type Rectangle.Builder<? extends Rectangle.Builder<?>>
. If you completely ignore the generics by removing the wildcard, this information is lost and the code will be compiled as ordinary pre-Java5.0 code (where generics did not exist). This is necessary for backward compatibility.
To see the difference, consider a subclass of Rectangle.Builder that ignores the generic 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 may warn you, that the class ignores the generic typing). So without the generic information, it is legal, to return the type Shape.Builder
from the opacity method. As soon as 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 the compiler error cannot find symbol
is, because the class Shape.Builder
does not itself declare the method/symbol T Shape.Builder#heigth()
, and the declared method T Shape.Builder#opacity()
does only guarantee, that the returned Object is of type Shape.Builder
, as declared in the type argument of class Shape.Builder<T extends Shape.Builder<T>>
. Therefore calling the method chain Rectangle.builder().opacity(0.5).height(250)
will only work, if Rectangle.builder()
is actually guaranteed to return a Builder, that is typed with a subclass of Rectangle.Builder. And this guarantee can only be given, if the generic typing is not ignored (as seen in the BadBuilder example).
When you add the method Shape.Builder#heigth
, by removing the comment in your code, this error obviously goes away, because then the Shape.Builder
object returned by Shape.Builder#opacity
will also have the corresponding method. You could also remove this error, by re-declaring Shape.Builder#opacity
in Rectangle.Builder like so:
@Override
public T opacity(double opacity) {
return super.opacity(opacity);
}
If you do this, then it is guaranteed, that the returned Object of T Rectangle.Builder#opacity()
is of type Rectangle.Builder
, as declared in the type arguments to class Rectangle.Builder<T extends Rectangle.Builder<T>> extends Shape.Builder<T>
.
Hope this helps.