Java 带有递归类型参数和抽象self方法的泛型类型如何允许方法链接正常工作?
我正在读有效的Java版本3。在第2章第14页中,作者讨论了生成器模式,并给出了以下代码:Java 带有递归类型参数和抽象self方法的泛型类型如何允许方法链接正常工作?,java,generics,design-patterns,builder,Java,Generics,Design Patterns,Builder,我正在读有效的Java版本3。在第2章第14页中,作者讨论了生成器模式,并给出了以下代码: public abstract class Pizza { public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE } final Set<Topping> toppings; abstract static class Builder<T extends Builder<T>> {
public abstract class Pizza {
public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
final Set<Topping> toppings;
abstract static class Builder<T extends Builder<T>> {
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
return self();
}
abstract Pizza build();
// Subclasses must override this method to return "this"
protected abstract T self();
}
Pizza(Builder<?> builder) {
toppings = builder.toppings.clone(); // See Item 50
}
}
书中引用:
请注意,Pizza.Builder是具有递归类型的泛型类型
参数这与抽象的self方法一起允许
链接可以在子类中正常工作,而不需要强制转换
现在我的问题是比萨类增加了什么力量/价值,它与其他类有何不同?如果你打算用简单的英语向一个五岁的孩子解释,你会怎么解释
我不明白在超类中抽象self方法的目的是什么
我之所以添加此部分是因为有注释部分
想象一下,我已经像这样更改了上述代码。仅出于说明目的,这将不是最好的示例:
我将NyPizza.Builder更改为通用:
public class NyPizza extends Pizza {
public enum Size { SMALL, MEDIUM, LARGE }
private final Size size;
public static class Builder<T> extends Pizza.Builder<Builder<T>> {
private final Size size;
public Builder(Size size) {
this.size = Objects.requireNonNull(size);
}
@Override public NyPizza build() {
return new NyPizza(this);
}
@Override protected Builder self() { return this; }
}
private NyPizza(Builder builder) {
super(builder);
size = builder.size;
}
}
并使用上述类,如下所示:
NyPizza pizza = new NyPizza.Builder(SMALL)
.addTopping(SAUSAGE).addTopping(ONION).build();
public abstract class Pizza {
public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
final Set<Topping> toppings;
abstract static class Builder<T extends Builder> {
T obj;
EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) {
toppings.add(Objects.requireNonNull(topping));
return self();
}
public T builder(){
return obj;
}
abstract Pizza build();
// Subclasses must override this method to return "this"
protected abstract T self();
}
Pizza(Builder<?> builder) {
toppings = builder.toppings.clone(); // See Item 50
}
}
nypisa.Builder测试=新建nypisa.BuilderSMALL.Builder
现在,因为我没有在这个表单中定义类生成器:类生成器Pizza类中的Builder方法应该不能检测t的类型是NyPizza.Builder,但它可以。怎么可能呢?如何将其更改为需要铸造?想象一个类:
class Foo<T extends Foo<T>> {
T foo() { ... }
}
然后这使T成为原始类型的Foo:Foo返回一个Foo,而不是Foo。因为原始类型会删除所有泛型,这意味着在foo.foo这样的调用链中,在第一次调用之后,您将丢失类型信息
在许多情况下,这可能不会让人觉得非常重要。但是此处原始类型的后果是:
若您并没有返回上面提到的self类型,实际上并没有任何东西可以阻止它返回其他类,那个么在第一次方法调用之后,您将丢失其他类。
如果在Foo中有其他泛型方法,例如List myList{…},则原始类型的Foo将返回原始列表,而不是List原始类型擦除所有泛型,而不仅仅是与省略的类型变量相关的泛型。
这些显然不适用于所有情况;但是,由于引入不必要的原始类型是一种不好的做法,因此请确保不要引入它们。想象一个类:
class Foo<T extends Foo<T>> {
T foo() { ... }
}
然后这使T成为原始类型的Foo:Foo返回一个Foo,而不是Foo。因为原始类型会删除所有泛型,这意味着在foo.foo这样的调用链中,在第一次调用之后,您将丢失类型信息
在许多情况下,这可能不会让人觉得非常重要。但是此处原始类型的后果是:
若您并没有返回上面提到的self类型,实际上并没有任何东西可以阻止它返回其他类,那个么在第一次方法调用之后,您将丢失其他类。
如果在Foo中有其他泛型方法,例如List myList{…},则原始类型的Foo将返回原始列表,而不是List原始类型擦除所有泛型,而不仅仅是与省略的类型变量相关的泛型。
这些显然不适用于所有情况;但是,由于引入不必要的原始类型只是一种不好的做法,所以请确保不要引入它们。为什么一个5岁的孩子会使用自绑定泛型;self用于避免在addTopping中将其强制转换为T-强制转换将触发类型安全警告为什么一个5岁的孩子使用自绑定泛型;self用于避免在addTopping中将其强制转换为T-该强制转换将触发类型安全警告谢谢。在中,它可以返回它自己,但实际上没有什么可以阻止它返回绑定内的其他类,当我们不使用t extends Foo并且只使用t extends Foo时,它们有相同的效果吗?我必须承认我没有弄明白这一部分,所有可能由t表示的类型也都是Foo的子类:你能添加更多的解释吗?t扩展Foo意味着返回t的方法将返回一个Foo,这是一个原始类型,也就是说,你失去了所有的泛型类型。谢谢!我理解您的观点,最后一个问题,在有效的java示例中,如果我使用类生成器之类的泛型,那么我就可以使用方法链接而无需任何强制转换。NyPizza pizza=new NyPizza.BuilderSMALL.addToppingSAUSAGE.addToppingOnOn.build;这里怎么样?因为nypisa.Builder不是泛型类:它没有类型参数。所有类型参数都在extends Pizza.Builder中指定。或者,您的意思是,为什么不在末尾强制转换为NyPizza?因为build方法有一个协变的返回类型:nyppizza是Pizza的一个子类,所以可以用nyppizza build覆盖Pizza build。谢谢。在中,它可以返回它自己,但实际上没有什么可以阻止它返回绑定内的其他类,当我们不使用t extends Foo并且只使用t extends Foo时,它们有相同的效果吗?我必须承认,我没有弄清楚这一部分,所有可能的类型都是由t表示的,也是Foo的子类:你能补充更多的解释吗?t扩展Foo意味着方法ret
urning T将返回一个Foo,这是一个原始类型,也就是说,您将丢失所有泛型类型。谢谢!我理解您的观点,最后一个问题,在有效的java示例中,如果我使用类生成器之类的泛型,那么我就可以使用方法链接而无需任何强制转换。NyPizza pizza=new NyPizza.BuilderSMALL.addToppingSAUSAGE.addToppingOnOn.build;这里怎么样?因为nypisa.Builder不是泛型类:它没有类型参数。所有类型参数都在extends Pizza.Builder中指定。或者,您的意思是,为什么不在末尾强制转换为NyPizza?因为build方法有一个协变的返回类型:nypisa是Pizza的子类,所以允许它用nypisa build覆盖Pizza build。
class Foo<T extends Foo> {
T foo() { ... }
}