Java 带有递归类型参数和抽象self方法的泛型类型如何允许方法链接正常工作?

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>> {

我正在读有效的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>> {
        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() { ... }
}