thl*_*lim 8 java generics design-patterns unbounded-wildcard
I was reading this article, , about subclassing a builder class. I understood the article but there was one small bit that bothered me. There was this method,
public static Builder<?> builder() {
return new Builder2();
}
Run Code Online (Sandbox Code Playgroud)
When I changed Builder<?> to Builder, a raw type, the compiler would not compile the code. The error was,
Rectangle.java:33: error: cannot find symbol
System.out.println(Rectangle.builder().opacity(0.5).height(250);
Run Code Online (Sandbox Code Playgroud)
What was the additional information passed to the compiler using the additional <?>? I suspected it was the compiler which could not figure the right instance during compilation. If I remove the comment markers in (A) the code compiled and ran fine. All the time it was referring to the Rectangle instance. So, my guess is it was the compiler which failed.
It would be great if someone can point me to an article that explains this or leads to find out more information to this. Thanks.
I have pasted the code here:
public class Shape {
private final double opacity;
public static class Builder<T extends Builder<T>> {
private double opacity;
public T opacity(double opacity) {
this.opacity = opacity;
return self();
}
/* Remove comment markers to make compilation works (A)
public T height(double height) {
System.out.println("height not set");
return self();
}
*/
protected T self() {
System.out.println("shape.self -> " + this);
return (T) this;
}
public Shape build() {
return new Shape(this);
}
}
public static Builder<?> builder() {
return new Builder();
}
protected Shape(Builder builder) {
this.opacity = builder.opacity;
}
}
public class Rectangle extends Shape {
private final double height;
public static class Builder<T extends Builder<T>> extends Shape.Builder<T> {
private double height;
public T height(double height) {
System.out.println("height is set");
this.height = height;
return self();
}
public Rectangle build() {
return new Rectangle(this);
}
}
public static Builder<?> builder() {
return new Builder();
}
protected Rectangle(Builder builder) {
super(builder);
this.height = builder.height;
}
public static void main(String[] args) {
Rectangle r = Rectangle.builder().opacity(0.5).height(250).build();
}
}
Run Code Online (Sandbox Code Playgroud)
使用附加信息传递给编译器的附加信息是什么
<?>?
使用通配符的附加信息<?>是,返回的Rectangle.Builder<?>是所有可能的泛型 Rectangle.Builder<T>类的超类(请参阅通配符).并且由于Rectangle.Builder<T>保证有一个类型参数T,它本身就是一个子类Rectangle.Builder,只要它的泛型类型不被忽略,Rectangle.Builder<?>也保证至少是类型Rectangle.Builder<? extends Rectangle.Builder<?>>.如果通过删除通配符完全忽略泛型,则此信息将丢失,代码将编译为普通的Java5.0之前的代码(其中泛型不存在).这是向后兼容性所必需的.
要查看差异,请考虑忽略泛型类型的Rectangle.Builder的子类:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,此类将覆盖Shape.Builder#opacity 而不返回其自身的子类.编译器不会为此类生成错误(但它可能会警告您,该类忽略了泛型类型).因此,如果没有通用信息,从不透明度方法返回类型是合法的Shape.Builder.只要向BadBuilder添加类型参数,此代码就不再编译:
public static class BadBuilder extends Rectangle.Builder<BadBuilder> // -> compile time error
Run Code Online (Sandbox Code Playgroud)
所以你得到编译器错误的原因cannot find symbol是,因为类Shape.Builder本身并不声明方法/符号T Shape.Builder#heigth(),并且声明的方法T Shape.Builder#opacity()只保证返回的Object是类型的Shape.Builder,如在类型参数中声明的那样class Shape.Builder<T extends Shape.Builder<T>>.因此,Rectangle.builder().opacity(0.5).height(250)只有Rectangle.builder()在确实返回一个使用Rectangle.Builder的子类键入的Builder时,调用方法链才有效.如果不忽略泛型类型(如BadBuilder示例中所示),则只能给出此保证.
添加方法时Shape.Builder#heigth,通过删除代码中的注释,此错误显然会消失,因为Shape.Builder返回的对象Shape.Builder#opacity也将具有相应的方法.您也可以通过Shape.Builder#opacity在Rectangle.Builder中重新声明来删除此错误,如下所示:
@Override
public T opacity(double opacity) {
return super.opacity(opacity);
}
Run Code Online (Sandbox Code Playgroud)
如果你这样做,那么保证返回的Object T Rectangle.Builder#opacity()是类型的 Rectangle.Builder,如在类型参数中声明的那样class Rectangle.Builder<T extends Rectangle.Builder<T>> extends Shape.Builder<T>.
希望这可以帮助.